glimmer-dsl-opal 0.0.2 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +704 -40
  3. data/VERSION +1 -1
  4. data/lib/glimmer-dsl-opal.rb +3 -2
  5. data/lib/glimmer/data_binding/element_binding.rb +1 -1
  6. data/lib/glimmer/data_binding/ext/observable_model.rb +40 -0
  7. data/lib/glimmer/data_binding/list_selection_binding.rb +51 -0
  8. data/lib/glimmer/dsl/opal/async_exec_expression.rb +17 -0
  9. data/lib/glimmer/dsl/opal/browser_expression.rb +17 -0
  10. data/lib/glimmer/dsl/opal/button_expression.rb +1 -0
  11. data/lib/glimmer/dsl/opal/dsl.rb +15 -1
  12. data/lib/glimmer/dsl/opal/grid_layout_expression.rb +17 -0
  13. data/lib/glimmer/dsl/opal/layout_data_expression.rb +17 -0
  14. data/lib/glimmer/dsl/opal/list_expression.rb +17 -0
  15. data/lib/glimmer/dsl/opal/list_selection_data_binding_expression.rb +42 -0
  16. data/lib/glimmer/dsl/opal/message_box_expression.rb +20 -0
  17. data/lib/glimmer/dsl/opal/observe_expression.rb +32 -0
  18. data/lib/glimmer/dsl/opal/property_expression.rb +6 -2
  19. data/lib/glimmer/dsl/opal/tab_folder_expression.rb +17 -0
  20. data/lib/glimmer/dsl/opal/tab_item_expression.rb +17 -0
  21. data/lib/glimmer/dsl/opal/text_expression.rb +22 -0
  22. data/lib/glimmer/opal/display_proxy.rb +23 -0
  23. data/lib/glimmer/opal/div_proxy.rb +9 -1
  24. data/lib/glimmer/opal/document_proxy.rb +131 -5
  25. data/lib/glimmer/opal/element_proxy.rb +246 -11
  26. data/lib/glimmer/opal/grid_layout_proxy.rb +54 -0
  27. data/lib/glimmer/opal/iframe_proxy.rb +23 -0
  28. data/lib/glimmer/opal/input_proxy.rb +16 -1
  29. data/lib/glimmer/opal/label_proxy.rb +2 -1
  30. data/lib/glimmer/opal/layout_data_proxy.rb +31 -0
  31. data/lib/glimmer/opal/list_proxy.rb +80 -0
  32. data/lib/glimmer/opal/modal.rb +94 -0
  33. data/lib/glimmer/opal/point.rb +5 -0
  34. data/lib/glimmer/opal/property_owner.rb +22 -0
  35. data/lib/glimmer/opal/select_proxy.rb +2 -1
  36. data/lib/glimmer/opal/tab_folder.rb +46 -0
  37. data/lib/glimmer/opal/tab_item.rb +98 -0
  38. data/lib/samples/elaborate/login.rb +0 -1
  39. data/lib/samples/elaborate/tic_tac_toe.rb +5 -5
  40. data/lib/samples/hello/hello_tab.rb +2 -2
  41. metadata +25 -2
@@ -3,10 +3,18 @@ require 'glimmer/opal/element_proxy'
3
3
  module Glimmer
4
4
  module Opal
5
5
  class DivProxy < ElementProxy
6
+ attr_reader :layout
7
+
8
+ def initialize(parent, args)
9
+ super(parent, args)
10
+ @layout = GridLayoutProxy.new(self, [])
11
+ end
12
+
6
13
  def dom
7
14
  div_id = id
15
+ div_style = css
8
16
  @dom ||= DOM {
9
- div(id: div_id, class: 'grid_layout')
17
+ div(id: div_id, class: 'grid-layout', style: div_style)
10
18
  }
11
19
  end
12
20
  end
@@ -1,9 +1,11 @@
1
1
  require 'glimmer/opal/element_proxy'
2
+ require 'glimmer/opal/point'
2
3
 
3
4
  module Glimmer
4
5
  module Opal
5
6
  class DocumentProxy < ElementProxy
6
7
  # TODO consider renaming to ShellProxy to match SWT API
8
+ attr_reader :minimum_size
7
9
 
8
10
  def initialize(args)
9
11
  @args = args
@@ -19,18 +21,139 @@ module Glimmer
19
21
  end
20
22
 
21
23
  def text=(value)
22
- $document.title = value
24
+ $document.ready do
25
+ $document.title = value
26
+ end
27
+ end
28
+
29
+ def minimum_size=(width_or_minimum_size, height = nil)
30
+ @minimum_size = height.nil? ? width_or_minimum_size : Point.new(width_or_minimum_size, height)
31
+ redraw
23
32
  end
24
33
 
25
34
  def head_dom
35
+ # TODO make grid-layout support grab excess space false
26
36
  @head_dom ||= DOM {
27
37
  head {
28
38
  <<~CSS
29
39
  <style>
30
- div.grid_layout > * {
31
- display: block;
32
- margin-bottom: 10px;
40
+ html {
41
+ width: 100%;
42
+ height: 100%;
43
+ }
44
+ body {
45
+ width: 100%;
46
+ height: 100%;
47
+ margin: 0;
48
+ }
49
+ body > iframe {
50
+ width: 100%;
51
+ height: 100%;
52
+ }
53
+ ul {
54
+ list-style: none;
55
+ padding: 0;
56
+ }
57
+ li {
58
+ cursor: default;
59
+ padding-left: 10px;
60
+ padding-right: 10px;
61
+ }
62
+ li.selected-list-item {
63
+ background: rgb(80, 116, 211);
64
+ color: white;
65
+ }
66
+ li.empty-list-item {
67
+ color: transparent;
68
+ }
69
+ .tabs {
70
+ overflow: hidden;
71
+ border: 1px solid #ccc;
72
+ background-color: #f1f1f1;
73
+ }
74
+
75
+ /* Style the buttons inside the tab */
76
+ .tabs .tab {
77
+ background-color: inherit;
78
+ float: left;
79
+ border: none;
80
+ outline: none;
81
+ cursor: pointer;
82
+ padding: 14px 16px;
83
+ transition: 0.3s;
84
+ font-size: 17px;
85
+ }
86
+
87
+ /* Change background color of buttons on hover */
88
+ .tabs .tab:hover {
89
+ background-color: #ddd;
90
+ }
91
+
92
+ /* Create an active/current tablink class */
93
+ .tabs .tab.active {
94
+ background-color: #ccc;
95
+ }
96
+
97
+ /* Style the tab content */
98
+ .tab-item {
99
+ padding: 6px 12px;
100
+ border: 1px solid #ccc;
101
+ border-top: none;
102
+ }
103
+
104
+ .hide {
105
+ display: none !important;
106
+ }
107
+
108
+ /* The Modal (background) */
109
+ .modal {
110
+ position: fixed; /* Stay in place */
111
+ z-index: 1; /* Sit on top */
112
+ padding-top: 100px; /* Location of the box */
113
+ left: 0;
114
+ top: 0;
115
+ width: 100%; /* Full width */
116
+ height: 100%; /* Full height */
117
+ overflow: auto; /* Enable scroll if needed */
118
+ background-color: rgb(0,0,0); /* Fallback color */
119
+ background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
120
+ text-align: center;
121
+ }
122
+
123
+ /* Modal Content */
124
+ .modal-content {
125
+ background-color: #fefefe;
126
+ margin: auto;
127
+ border: 1px solid #888;
128
+ display: inline-block;
129
+ min-width: 200px;
130
+ }
131
+
132
+ .modal-content .text {
133
+ background: rgb(80, 116, 211);
134
+ color: white;
135
+ padding: 5px;
136
+ }
137
+
138
+ .modal-content .message {
139
+ padding: 20px;
140
+ }
141
+
142
+ /* The Close Button */
143
+ .close {
144
+ color: #aaaaaa;
145
+ float: right;
146
+ # font-size: 18px;
147
+ font-weight: bold;
148
+ margin: 5px;
33
149
  }
150
+
151
+ .close:hover,
152
+ .close:focus {
153
+ color: #000;
154
+ text-decoration: none;
155
+ cursor: pointer;
156
+ }
34
157
  </style>
35
158
  CSS
36
159
  }
@@ -38,8 +161,11 @@ module Glimmer
38
161
  end
39
162
 
40
163
  def dom
164
+ i = 0
165
+ body_style = ''
166
+ body_style += "min-width: #{@minimum_size.x}px; min-height: #{@minimum_size.y}px;" if @minimum_size
41
167
  @dom ||= DOM {
42
- body {
168
+ body(style: body_style) {
43
169
  }
44
170
  }
45
171
  end
@@ -1,25 +1,45 @@
1
+ require 'glimmer/opal/property_owner'
2
+
1
3
  module Glimmer
2
4
  module Opal
3
5
  class ElementProxy
4
- attr_reader :parent, :args
6
+ include Glimmer
7
+ include PropertyOwner
8
+ attr_reader :parent, :args, :css_classes, :css, :children, :enabled
5
9
 
6
10
  def initialize(parent, args)
7
11
  @parent = parent
8
12
  @args = args
9
- @children = []
13
+ @children = Set.new
14
+ @css_classes = Set.new
15
+ @css = ''
16
+ @enabled = true
10
17
  @parent.add_child(self)
11
18
  end
12
19
 
13
20
  def add_child(child)
14
- return if @children.include?(child)
21
+ # return if @children.include?(child) # TODO consider adding an option to enable this if needed to prevent dom repetition
15
22
  @children << child
16
23
  dom << child.dom
17
24
  end
25
+
26
+ def enabled=(value)
27
+ @enabled = value
28
+ redraw
29
+ end
18
30
 
19
31
  def redraw
20
- old_dom = @dom
21
- @dom = nil
22
- old_dom.replace dom
32
+ if @dom
33
+ old_dom = @dom
34
+ @dom = nil
35
+ old_dom.replace dom
36
+ else
37
+ dom
38
+ end
39
+ @children.each do |child|
40
+ child.redraw
41
+ add_child(child)
42
+ end
23
43
  end
24
44
 
25
45
  # Subclasses must override with their own mappings
@@ -32,6 +52,7 @@ module Glimmer
32
52
  end
33
53
 
34
54
  def id
55
+ # TODO replace hash with autoincrement per name
35
56
  "#{name}-#{hash}"
36
57
  end
37
58
 
@@ -40,13 +61,227 @@ module Glimmer
40
61
  "#{name}##{id}"
41
62
  end
42
63
 
64
+ def add_css_class(css_class)
65
+ @css_classes << css_class
66
+ redraw
67
+ end
68
+
69
+ def add_css_classes(css_classes)
70
+ @css_classes += css_classes
71
+ redraw
72
+ end
73
+
74
+ def remove_css_class(css_class)
75
+ @css_classes.delete(css_class)
76
+ redraw
77
+ end
78
+
79
+ def remove_css_classes(css_classes)
80
+ @css_classes -= css_classes
81
+ redraw
82
+ end
83
+
84
+ def clear_css_classes(css_class)
85
+ @css_classes.clear
86
+ redraw
87
+ end
88
+
89
+ def css=(css)
90
+ @css = css
91
+ redraw
92
+ end
93
+
94
+ def has_style?(symbol)
95
+ @args.include?(symbol) # not a very solid implementation. Bring SWT constants eventually
96
+ end
97
+
43
98
  def handle_observation_request(keyword, &event_listener)
44
- event = observation_request_to_event_mapping[keyword][:event]
45
- event_handler = observation_request_to_event_mapping[keyword][:event_handler]
46
- event_listener = event_handler&.call(event_listener) || event_listener
47
- delegate = $document.on(event, selector, &event_listener)
99
+ return unless observation_request_to_event_mapping.keys.include?(keyword)
100
+ event = nil
101
+ delegate = nil
102
+ [observation_request_to_event_mapping[keyword]].flatten.each do |mapping|
103
+ event = mapping[:event]
104
+ event_handler = mapping[:event_handler]
105
+ potential_event_listener = event_handler&.call(event_listener)
106
+ event_listener = event_handler&.call(event_listener) || event_listener
107
+ delegate = $document.on(event, selector, &event_listener)
108
+ end
48
109
  EventListenerProxy.new(element_proxy: self, event: event, selector: selector, delegate: delegate)
49
- end
110
+ end
111
+
112
+ def add_observer(observer, property_name)
113
+ property_listener_installers = self.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
114
+ widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
115
+ widget_listener_installers.to_a.each do |widget_listener_installer|
116
+ widget_listener_installer.call(observer)
117
+ end
118
+ end
119
+
120
+ def set_attribute(attribute_name, *args)
121
+ apply_property_type_converters(attribute_name, args)
122
+ super(attribute_name, *args)
123
+ end
124
+
125
+ def apply_property_type_converters(attribute_name, args)
126
+ if args.count == 1
127
+ value = args.first
128
+ converter = property_type_converters[attribute_name.to_sym]
129
+ args[0] = converter.call(value) if converter
130
+ end
131
+ # if args.count == 1 && args.first.is_a?(ColorProxy)
132
+ # g_color = args.first
133
+ # args[0] = g_color.swt_color
134
+ # end
135
+ end
136
+
137
+ def property_type_converters
138
+ @property_type_converters ||= {
139
+ # :background => color_converter,
140
+ # :background_image => lambda do |value|
141
+ # if value.is_a?(String)
142
+ # if value.start_with?('uri:classloader')
143
+ # value = value.sub(/^uri\:classloader\:\//, '')
144
+ # object = java.lang.Object.new
145
+ # value = object.java_class.resource_as_stream(value)
146
+ # value = java.io.BufferedInputStream.new(value)
147
+ # end
148
+ # image_data = ImageData.new(value)
149
+ # on_event_Resize do |resize_event|
150
+ # new_image_data = image_data.scaledTo(@swt_widget.getSize.x, @swt_widget.getSize.y)
151
+ # @swt_widget.getBackgroundImage&.dispose
152
+ # @swt_widget.setBackgroundImage(Image.new(@swt_widget.getDisplay, new_image_data))
153
+ # end
154
+ # Image.new(@swt_widget.getDisplay, image_data)
155
+ # else
156
+ # value
157
+ # end
158
+ # end,
159
+ # :foreground => color_converter,
160
+ # :font => lambda do |value|
161
+ # if value.is_a?(Hash)
162
+ # font_properties = value
163
+ # FontProxy.new(self, font_properties).swt_font
164
+ # else
165
+ # value
166
+ # end
167
+ # end,
168
+ # :items => lambda do |value|
169
+ # value.to_java :string
170
+ # end,
171
+ :text => lambda do |value|
172
+ # if swt_widget.is_a?(Browser)
173
+ # value.to_s
174
+ # else
175
+ value.to_s
176
+ # end
177
+ end,
178
+ # :visible => lambda do |value|
179
+ # !!value
180
+ # end,
181
+ }
182
+ end
183
+
184
+ def widget_property_listener_installers
185
+ @swt_widget_property_listener_installers ||= {
186
+ # ElementProxy => {
187
+ # :focus => lambda do |observer|
188
+ # on_focus_gained { |focus_event|
189
+ # observer.call(true)
190
+ # }
191
+ # on_focus_lost { |focus_event|
192
+ # observer.call(false)
193
+ # }
194
+ # end,
195
+ # },
196
+ InputProxy => {
197
+ :text => lambda do |observer|
198
+ on_modify_text { |modify_event|
199
+ observer.call(text)
200
+ }
201
+ end,
202
+ # :caret_position => lambda do |observer|
203
+ # on_event_keydown { |event|
204
+ # observer.call(getCaretPosition)
205
+ # }
206
+ # on_event_keyup { |event|
207
+ # observer.call(getCaretPosition)
208
+ # }
209
+ # on_event_mousedown { |event|
210
+ # observer.call(getCaretPosition)
211
+ # }
212
+ # on_event_mouseup { |event|
213
+ # observer.call(getCaretPosition)
214
+ # }
215
+ # end,
216
+ # :selection => lambda do |observer|
217
+ # on_event_keydown { |event|
218
+ # observer.call(getSelection)
219
+ # }
220
+ # on_event_keyup { |event|
221
+ # observer.call(getSelection)
222
+ # }
223
+ # on_event_mousedown { |event|
224
+ # observer.call(getSelection)
225
+ # }
226
+ # on_event_mouseup { |event|
227
+ # observer.call(getSelection)
228
+ # }
229
+ # end,
230
+ # :selection_count => lambda do |observer|
231
+ # on_event_keydown { |event|
232
+ # observer.call(getSelectionCount)
233
+ # }
234
+ # on_event_keyup { |event|
235
+ # observer.call(getSelectionCount)
236
+ # }
237
+ # on_event_mousedown { |event|
238
+ # observer.call(getSelectionCount)
239
+ # }
240
+ # on_event_mouseup { |event|
241
+ # observer.call(getSelectionCount)
242
+ # }
243
+ # end,
244
+ # :top_index => lambda do |observer|
245
+ # @last_top_index = getTopIndex
246
+ # on_paint_control { |event|
247
+ # if getTopIndex != @last_top_index
248
+ # @last_top_index = getTopIndex
249
+ # observer.call(@last_top_index)
250
+ # end
251
+ # }
252
+ # end,
253
+ },
254
+ # Java::OrgEclipseSwtCustom::StyledText => {
255
+ # :text => lambda do |observer|
256
+ # on_modify_text { |modify_event|
257
+ # observer.call(getText)
258
+ # }
259
+ # end,
260
+ # },
261
+ # InputProxy => {
262
+ # :selection => lambda do |observer|
263
+ # on_widget_selected { |selection_event|
264
+ # observer.call(getSelection)
265
+ # }
266
+ # end
267
+ # },
268
+ # Java::OrgEclipseSwtWidgets::MenuItem => {
269
+ # :selection => lambda do |observer|
270
+ # on_widget_selected { |selection_event|
271
+ # observer.call(getSelection)
272
+ # }
273
+ # end
274
+ # },
275
+ # Java::OrgEclipseSwtWidgets::Spinner => {
276
+ # :selection => lambda do |observer|
277
+ # on_widget_selected { |selection_event|
278
+ # observer.call(getSelection)
279
+ # }
280
+ # end
281
+ # },
282
+ }
283
+ end
284
+
50
285
  end
51
286
  end
52
287
  end