glimmer-dsl-swt 4.17.4.0 → 4.17.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +191 -29
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +18 -5
  6. data/lib/glimmer/dsl/swt/checkbox_group_selection_data_binding_expression.rb +61 -0
  7. data/lib/glimmer/dsl/swt/custom_widget_expression.rb +2 -0
  8. data/lib/glimmer/dsl/swt/dsl.rb +2 -0
  9. data/lib/glimmer/dsl/swt/expand_item_expression.rb +60 -0
  10. data/lib/glimmer/dsl/swt/radio_group_selection_data_binding_expression.rb +61 -0
  11. data/lib/glimmer/dsl/swt/widget_expression.rb +1 -0
  12. data/lib/glimmer/swt/custom/checkbox_group.rb +160 -0
  13. data/lib/glimmer/swt/custom/code_text.rb +20 -13
  14. data/lib/glimmer/swt/custom/radio_group.rb +155 -0
  15. data/lib/glimmer/swt/expand_item_proxy.rb +97 -0
  16. data/lib/glimmer/swt/image_proxy.rb +5 -0
  17. data/lib/glimmer/swt/menu_proxy.rb +1 -1
  18. data/lib/glimmer/swt/sash_form_proxy.rb +1 -1
  19. data/lib/glimmer/swt/styled_text_proxy.rb +43 -0
  20. data/lib/glimmer/swt/tab_item_proxy.rb +1 -1
  21. data/lib/glimmer/swt/widget_proxy.rb +94 -35
  22. data/lib/glimmer/ui/custom_widget.rb +3 -0
  23. data/samples/elaborate/meta_sample.rb +36 -31
  24. data/samples/hello/hello_checkbox.rb +85 -0
  25. data/samples/hello/hello_checkbox_group.rb +68 -0
  26. data/samples/hello/hello_combo.rb +12 -12
  27. data/samples/hello/hello_expand_bar.rb +110 -0
  28. data/samples/hello/hello_list_multi_selection.rb +23 -23
  29. data/samples/hello/hello_list_single_selection.rb +14 -13
  30. data/samples/hello/hello_radio.rb +108 -0
  31. data/samples/hello/hello_radio_group.rb +84 -0
  32. data/samples/hello/hello_styled_text.rb +138 -0
  33. metadata +17 -4
@@ -0,0 +1,61 @@
1
+ # Copyright (c) 2007-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
+ require 'glimmer/data_binding/model_binding'
24
+ require 'glimmer/data_binding/widget_binding'
25
+
26
+ module Glimmer
27
+ module DSL
28
+ module SWT
29
+ class CheckboxGroupSelectionDataBindingExpression < Expression
30
+
31
+ def can_interpret?(parent, keyword, *args, &block)
32
+ keyword == 'selection' and
33
+ block.nil? and
34
+ (parent.is_a?(Glimmer::SWT::Custom::CheckboxGroup) or (parent.is_a?(Glimmer::UI::CustomWidget) and parent.body_root.is_a?(Glimmer::SWT::Custom::CheckboxGroup)) ) and
35
+ args.size == 1 and
36
+ args[0].is_a?(DataBinding::ModelBinding) and
37
+ args[0].evaluate_options_property.is_a?(Array)
38
+ end
39
+
40
+ def interpret(parent, keyword, *args, &block)
41
+ model_binding = args[0]
42
+
43
+ #TODO make this options observer dependent and all similar observers in widget specific data binding handlers
44
+ # TODO consider delegating some of this work
45
+ widget_binding = DataBinding::WidgetBinding.new(parent, 'items')
46
+ widget_binding.call(model_binding.evaluate_options_property)
47
+ model = model_binding.base_model
48
+ widget_binding.observe(model, model_binding.options_property_name)
49
+
50
+ widget_binding = DataBinding::WidgetBinding.new(parent, 'selection')
51
+ widget_binding.call(model_binding.evaluate_property)
52
+ widget_binding.observe(model, model_binding.property_name_expression)
53
+
54
+ parent.on_widget_selected do
55
+ model_binding.call(widget_binding.evaluate_property)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -26,6 +26,8 @@ require 'glimmer/dsl/top_level_expression'
26
26
  require 'glimmer/ui/custom_widget'
27
27
  require 'glimmer/ui/custom_shell'
28
28
  require 'glimmer/swt/custom/code_text'
29
+ require 'glimmer/swt/custom/radio_group'
30
+ require 'glimmer/swt/custom/checkbox_group'
29
31
 
30
32
  module Glimmer
31
33
  module DSL
@@ -41,6 +41,8 @@ module Glimmer
41
41
  layout
42
42
  widget_listener
43
43
  combo_selection_data_binding
44
+ checkbox_group_selection_data_binding
45
+ radio_group_selection_data_binding
44
46
  list_selection_data_binding
45
47
  tree_items_data_binding
46
48
  table_items_data_binding
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2007-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'
23
+ require 'glimmer/dsl/static_expression'
24
+ require 'glimmer/dsl/parent_expression'
25
+ require 'glimmer/swt/widget_proxy'
26
+ require 'glimmer/swt/expand_item_proxy'
27
+
28
+ module Glimmer
29
+ module DSL
30
+ module SWT
31
+ class ExpandItemExpression < StaticExpression
32
+ include ParentExpression
33
+
34
+ include_package 'org.eclipse.swt.widgets'
35
+
36
+ def can_interpret?(parent, keyword, *args, &block)
37
+ initial_condition = (keyword == 'expand_item') and parent.respond_to?(:swt_widget)
38
+ if initial_condition
39
+ if parent.swt_widget.is_a?(ExpandBar)
40
+ return true
41
+ else
42
+ Glimmer::Config.logger.error {"expand_item widget may only be used directly under a expand_bar widget!"}
43
+ end
44
+ end
45
+ false
46
+ end
47
+
48
+ def interpret(parent, keyword, *args, &block)
49
+ Glimmer::SWT::ExpandItemProxy.new(parent, args)
50
+ end
51
+
52
+ def add_content(parent, &block)
53
+ super
54
+ parent.post_add_content
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,61 @@
1
+ # Copyright (c) 2007-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
+ require 'glimmer/data_binding/model_binding'
24
+ require 'glimmer/data_binding/widget_binding'
25
+
26
+ module Glimmer
27
+ module DSL
28
+ module SWT
29
+ class RadioGroupSelectionDataBindingExpression < Expression
30
+
31
+ def can_interpret?(parent, keyword, *args, &block)
32
+ keyword == 'selection' and
33
+ block.nil? and
34
+ (parent.is_a?(Glimmer::SWT::Custom::RadioGroup) or (parent.is_a?(Glimmer::UI::CustomWidget) and parent.body_root.is_a?(Glimmer::SWT::Custom::RadioGroup)) ) and
35
+ args.size == 1 and
36
+ args[0].is_a?(DataBinding::ModelBinding) and
37
+ args[0].evaluate_options_property.is_a?(Array)
38
+ end
39
+
40
+ def interpret(parent, keyword, *args, &block)
41
+ model_binding = args[0]
42
+
43
+ #TODO make this options observer dependent and all similar observers in widget specific data binding handlers
44
+ # TODO consider delegating some of this work
45
+ widget_binding = DataBinding::WidgetBinding.new(parent, 'items')
46
+ widget_binding.call(model_binding.evaluate_options_property)
47
+ model = model_binding.base_model
48
+ widget_binding.observe(model, model_binding.options_property_name)
49
+
50
+ widget_binding = DataBinding::WidgetBinding.new(parent, 'selection')
51
+ widget_binding.call(model_binding.evaluate_property)
52
+ widget_binding.observe(model, model_binding.property_name_expression)
53
+
54
+ parent.on_widget_selected do
55
+ model_binding.call(widget_binding.evaluate_property)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -57,3 +57,4 @@ require 'glimmer/swt/tree_proxy'
57
57
  require 'glimmer/swt/table_proxy'
58
58
  require 'glimmer/swt/table_column_proxy'
59
59
  require 'glimmer/swt/sash_form_proxy'
60
+ require 'glimmer/swt/styled_text_proxy'
@@ -0,0 +1,160 @@
1
+ require 'glimmer/ui/custom_widget'
2
+
3
+ module Glimmer
4
+ module SWT
5
+ module Custom
6
+ # CodeText is a customization of StyledText with support for Ruby Syntax Highlighting
7
+ class CheckboxGroup
8
+ include Glimmer::UI::CustomWidget
9
+
10
+ body {
11
+ composite # just an empty composite to hold checkboxs upon data-binding `selection`
12
+ }
13
+
14
+ def items=(text_array)
15
+ selection_value = selection
16
+ @items = Array[*text_array]
17
+ build_checkboxes
18
+ end
19
+
20
+ def items
21
+ @items || []
22
+ end
23
+
24
+ def selection=(selection_texts)
25
+ items.count.times do |index|
26
+ checkbox = checkboxes[index]
27
+ item = items[index]
28
+ label_text = labels[index]&.text
29
+ checkbox.selection = selection_texts.to_a.include?(label_text)
30
+ end
31
+ selection_texts
32
+ end
33
+
34
+ def selection
35
+ selection_indices.map do |selection_index|
36
+ labels[selection_index]&.text
37
+ end
38
+ end
39
+
40
+ def selection_indices=(indices)
41
+ self.selection=(indices.to_a.map {|index| items[index]})
42
+ end
43
+ alias select selection_indices=
44
+
45
+ def selection_indices
46
+ checkboxes.each_with_index.map do |checkbox, index|
47
+ index if checkbox.selection
48
+ end.to_a.compact
49
+ end
50
+
51
+ def checkboxes
52
+ @checkboxes ||= []
53
+ end
54
+ alias checks checkboxes
55
+
56
+ def labels
57
+ @labels ||= []
58
+ end
59
+
60
+ def can_handle_observation_request?(observation_request)
61
+ checkboxes.first&.can_handle_observation_request?(observation_request) || super(observation_request)
62
+ end
63
+
64
+ def handle_observation_request(observation_request, &block)
65
+ observation_requests << [observation_request, block]
66
+ delegate_observation_request_to_checkboxes(observation_request, &block)
67
+ super
68
+ end
69
+
70
+ def delegate_observation_request_to_checkboxes(observation_request, &block)
71
+ if observation_request != 'on_widget_disposed'
72
+ checkboxes.count.times do |index|
73
+ checkbox = checkboxes[index]
74
+ label = labels[index]
75
+ listener_block = lambda do |event|
76
+ event.widget = self.swt_widget
77
+ block.call(event)
78
+ end
79
+ if observation_request == 'on_widget_selected'
80
+ checkbox.handle_observation_request(observation_request, &listener_block) if checkbox.can_handle_observation_request?(observation_request)
81
+ label.handle_observation_request('on_mouse_up', &listener_block)
82
+ else
83
+ checkbox.handle_observation_request(observation_request, &listener_block) if checkbox.can_handle_observation_request?(observation_request)
84
+ label.handle_observation_request(observation_request, &listener_block) if label.can_handle_observation_request?(observation_request)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def observation_requests
91
+ @observation_requests ||= Set.new
92
+ end
93
+
94
+ def has_attribute?(attribute_name, *args)
95
+ (@composites.to_a + @checkboxes.to_a + @labels.to_a).map do |widget_proxy|
96
+ return true if widget_proxy.has_attribute?(attribute_name, *args)
97
+ end
98
+ super
99
+ end
100
+
101
+ def set_attribute(attribute_name, *args)
102
+ excluded_attributes = ['selection']
103
+ unless excluded_attributes.include?(attribute_name.to_s)
104
+ (@composites.to_a + @checkboxes.to_a + @labels.to_a).each do |widget_proxy|
105
+ widget_proxy.set_attribute(attribute_name, *args) if widget_proxy.has_attribute?(attribute_name, *args)
106
+ end
107
+ end
108
+ super
109
+ end
110
+
111
+ private
112
+
113
+ def build_checkboxes
114
+ current_selection = selection
115
+ @composites.to_a.each(&:dispose)
116
+ @checkboxes = []
117
+ @labels = []
118
+ @composites = []
119
+ items.each do |item|
120
+ body_root.content {
121
+ @composites << composite {
122
+ grid_layout(2, false) {
123
+ margin_width 0
124
+ margin_height 0
125
+ horizontal_spacing 0
126
+ vertical_spacing 0
127
+ }
128
+ checkboxes << checkbox { |checkbox_proxy|
129
+ on_widget_selected {
130
+ self.selection_indices = checkboxes.each_with_index.map {|cb, i| i if cb.selection}.to_a.compact
131
+ }
132
+ }
133
+ labels << label { |label_proxy|
134
+ layout_data :fill, :center, true, false
135
+ text item
136
+ on_mouse_up { |event|
137
+ found_text = labels.each_with_index.detect {|l, i| event.widget == l.swt_widget}[0]&.text
138
+ selection_values = self.selection
139
+ if selection_values.include?(found_text)
140
+ selection_values.delete(found_text)
141
+ else
142
+ selection_values << found_text
143
+ end
144
+ self.selection = selection_values
145
+ }
146
+ }
147
+ }
148
+ }
149
+ end
150
+ observation_requests.to_a.each do |observation_request, block|
151
+ delegate_observation_request_to_checkboxes(observation_request, &block)
152
+ end
153
+ self.selection = current_selection
154
+ end
155
+ end
156
+
157
+ CheckGroup = CheckboxGroup
158
+ end
159
+ end
160
+ end
@@ -38,11 +38,13 @@ module Glimmer
38
38
  swt_widget&.text
39
39
  end
40
40
 
41
- def syntax_highlighting
41
+ def syntax_highlighting
42
42
  return [] if text.to_s.strip.empty?
43
43
  code = text
44
- lexer = Rouge::Lexer.find_fancy('ruby', code)
45
- lex = lexer.lex(code).to_a
44
+ return @syntax_highlighting if @last_code == code
45
+ @last_code = code
46
+ @lexer ||= Rouge::Lexer.find_fancy('ruby', code)
47
+ lex = @lexer.lex(code).to_a
46
48
  code_size = 0
47
49
  lex_hashes = lex.map do |pair|
48
50
  {token_type: pair.first, token_text: pair.last}
@@ -50,7 +52,15 @@ module Glimmer
50
52
  hash[:token_index] = code_size
51
53
  code_size += hash[:token_text].size
52
54
  end
53
- # TODO consider grouping by line to optimize performance for line get style listener
55
+ code_lines = code.split("\n")
56
+ line_index = 0
57
+ @syntax_highlighting = code_lines_map = code_lines.reduce({}) do |hash, line|
58
+ line_hashes = []
59
+ line_hashes << lex_hashes.shift while lex_hashes.any? && lex_hashes.first[:token_index].between?(line_index, line_index + line.size)
60
+ hash.merge(line_index => line_hashes).tap do
61
+ line_index += line.size + 1
62
+ end
63
+ end
54
64
  end
55
65
 
56
66
  before_body {
@@ -68,15 +78,12 @@ module Glimmer
68
78
 
69
79
  on_line_get_style { |line_style_event|
70
80
  styles = []
71
- line_style_event.lineOffset
72
- syntax_highlighting.each do |token_hash|
73
- if token_hash[:token_index] >= line_style_event.lineOffset && token_hash[:token_index] < (line_style_event.lineOffset + line_style_event.lineText.size)
74
- start_index = token_hash[:token_index]
75
- size = token_hash[:token_text].size
76
- token_color = SYNTAX_COLOR_MAP[token_hash[:token_type].name] || [:black]
77
- token_color = color(*token_color).swt_color
78
- styles << StyleRange.new(start_index, size, token_color, nil)
79
- end
81
+ syntax_highlighting[line_style_event.lineOffset].to_a.each do |token_hash|
82
+ start_index = token_hash[:token_index]
83
+ size = token_hash[:token_text].size
84
+ token_color = SYNTAX_COLOR_MAP[token_hash[:token_type].name] || [:black]
85
+ token_color = color(*token_color).swt_color
86
+ styles << StyleRange.new(start_index, size, token_color, nil)
80
87
  end
81
88
  line_style_event.styles = styles.to_java(StyleRange) unless styles.empty?
82
89
  }
@@ -0,0 +1,155 @@
1
+ require 'glimmer/ui/custom_widget'
2
+
3
+ module Glimmer
4
+ module SWT
5
+ module Custom
6
+ # CodeText is a customization of StyledText with support for Ruby Syntax Highlighting
7
+ class RadioGroup
8
+ include Glimmer::UI::CustomWidget
9
+
10
+ body {
11
+ composite # just an empty composite to hold radios upon data-binding `selection`
12
+ }
13
+
14
+ def items=(text_array)
15
+ selection_value = selection
16
+ @items = Array[*text_array]
17
+ build_radios
18
+ end
19
+
20
+ def items
21
+ @items || []
22
+ end
23
+
24
+ def selection=(text)
25
+ radios.count.times do |index|
26
+ radio = radios[index]
27
+ item = items[index]
28
+ radio.selection = item == text
29
+ end
30
+ end
31
+
32
+ def selection
33
+ selection_value = labels[selection_index]&.text unless selection_index == -1
34
+ selection_value.to_s
35
+ end
36
+
37
+ def selection_index=(index)
38
+ self.selection=(items[index])
39
+ end
40
+ alias select selection_index=
41
+
42
+ def selection_index
43
+ radios.index(radios.detect(&:selection)) || -1
44
+ end
45
+
46
+ def radios
47
+ @radios ||= []
48
+ end
49
+
50
+ def labels
51
+ @labels ||= []
52
+ end
53
+
54
+ def can_handle_observation_request?(observation_request)
55
+ radios.first&.can_handle_observation_request?(observation_request) || super(observation_request)
56
+ end
57
+
58
+ def handle_observation_request(observation_request, &block)
59
+ observation_requests << [observation_request, block]
60
+ delegate_observation_request_to_radios(observation_request, &block)
61
+ super
62
+ end
63
+
64
+ def delegate_observation_request_to_radios(observation_request, &block)
65
+ if observation_request != 'on_widget_disposed'
66
+ radios.count.times do |index|
67
+ radio = radios[index]
68
+ label = labels[index]
69
+ if observation_request == 'on_widget_selected'
70
+ radio_block = lambda do |event|
71
+ if event.widget.selection || selection_index == -1
72
+ event.widget = self.swt_widget
73
+ block.call(event)
74
+ end
75
+ end
76
+ label_block = lambda do |event|
77
+ self.selection_index = index
78
+ block.call(event)
79
+ end
80
+ radio.handle_observation_request(observation_request, &radio_block) if radio.can_handle_observation_request?(observation_request)
81
+ label.handle_observation_request('on_mouse_up', &label_block)
82
+ else
83
+ listener_block = lambda do |event|
84
+ event.widget = self.swt_widget
85
+ block.call(event)
86
+ end
87
+ radio.handle_observation_request(observation_request, &listener_block) if radio.can_handle_observation_request?(observation_request)
88
+ label.handle_observation_request(observation_request, &listener_block) if label.can_handle_observation_request?(observation_request)
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ def observation_requests
95
+ @observation_requests ||= Set.new
96
+ end
97
+
98
+ def has_attribute?(attribute_name, *args)
99
+ (@composites.to_a + @radios.to_a + @labels.to_a).map do |widget_proxy|
100
+ return true if widget_proxy.has_attribute?(attribute_name, *args)
101
+ end
102
+ super
103
+ end
104
+
105
+ def set_attribute(attribute_name, *args)
106
+ excluded_attributes = ['selection']
107
+ unless excluded_attributes.include?(attribute_name.to_s)
108
+ (@composites.to_a + @radios.to_a + @labels.to_a).each do |widget_proxy|
109
+ widget_proxy.set_attribute(attribute_name, *args) if widget_proxy.has_attribute?(attribute_name, *args)
110
+ end
111
+ end
112
+ super
113
+ end
114
+
115
+ private
116
+
117
+ def build_radios
118
+ current_selection = selection
119
+ @composites.to_a.each(&:dispose)
120
+ @radios = []
121
+ @labels = []
122
+ @composites = []
123
+ items.each do |item|
124
+ body_root.content {
125
+ @composites << composite {
126
+ grid_layout(2, false) {
127
+ margin_width 0
128
+ margin_height 0
129
+ horizontal_spacing 0
130
+ vertical_spacing 0
131
+ }
132
+ radios << radio { |radio_proxy|
133
+ on_widget_selected {
134
+ self.selection = items[radios.index(radio_proxy)]
135
+ }
136
+ }
137
+ labels << label { |label_proxy|
138
+ layout_data :fill, :center, true, false
139
+ text item
140
+ on_mouse_up {
141
+ self.selection = label_proxy.text
142
+ }
143
+ }
144
+ }
145
+ }
146
+ end
147
+ observation_requests.to_a.each do |observation_request, block|
148
+ delegate_observation_request_to_radios(observation_request, &block)
149
+ end
150
+ self.selection = current_selection
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end