glimmer-dsl-opal 0.7.0 → 0.7.1

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: 15339e6707f761011f074d030dc58a41733c3ea249da444745c1c6696ab90468
4
- data.tar.gz: 440722c63a85a92c02dac9a0955bcb5b26d620be1338727521674d63a3d5c9b6
3
+ metadata.gz: b4e81c8aae870295468f5c0e573da962daa58837efda4742c9855313ae29f42a
4
+ data.tar.gz: 40df246c27977d3fa862a452cdfb2dd99ad0b506374408c69e25666b3fc7fa50
5
5
  SHA512:
6
- metadata.gz: 7daddcebb68f6d612c011f2d99c3af01073f3740a3fb82bd9a4a1bd96c748db358c61c6d3ad1502aa23f5061fb8041921c680a729bf39d0fa971cfa55059e4d7
7
- data.tar.gz: 15cea650eacfb8dbb05dc4892754b160cd0ad5ec0d5e5c0a5c87636e1fbc69f43d9a3fcc250edd3c9febc2bae1ab0a3ecf9fe47f295363de9b13cc47e86debc3
6
+ metadata.gz: c46c3bdfcc129557ac54640d2d905cb7495f010ff1b81422c5b55529a01ecb67c698b4724029b62b9cbc6bcb46727763185ba1b4469c0355259d16acd1f228f1
7
+ data.tar.gz: 803c487121b4092f024fabca666e359fcdfeca87482b95a391e6b857b19dca28f46a845fc46e6c189e73254c1aa3a1f53d8cbe51c113f425be5019544a353913
@@ -1,5 +1,12 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.7.1
4
+
5
+ - Combo table editor (enabled in Hello, Table! sample)
6
+ - Fix issue with table cell selection for editing not working
7
+ - Remove widget from parent upon dispose
8
+ - Remove listeners upon widget dispose
9
+
3
10
  ## 0.7.0
4
11
 
5
12
  - Hello, Table! Sample
@@ -127,3 +134,4 @@
127
134
 
128
135
  - Initial support for webifying Glimmer SWT apps
129
136
  - Support for Shell and Label widgets (text property only).
137
+ - Hello, World! sample support
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Opal 0.7.0 (Pure Ruby Web GUI)
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Opal 0.7.1 (Pure Ruby Web GUI)
2
2
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-opal.svg)](http://badge.fury.io/rb/glimmer-dsl-opal)
3
3
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
4
 
@@ -8,11 +8,11 @@
8
8
 
9
9
  Use in one of two ways:
10
10
  - **Direct:** build the GUI of web apps with the same friendly desktop GUI Ruby syntax as [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt), thus requiring a lot less code than web technologies that is in pure Ruby and avoiding opaque web concepts like 'render' and 'reactive'. No HTML/JS/CSS skills are even required. Web designers may be involved with CSS styling only if needed.
11
- - **Adapter:** Auto-webify [Glimmer](https://github.com/AndyObtiva/glimmer) desktop apps (i.e. apps built with [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)) via [Opal](https://opalrb.com/) on [Rails](https://rubyonrails.org/) without changing a line of code. Apps may then be custom-styled by web designers for the web with standard CSS.
11
+ - **Adapter:** auto-webify [Glimmer](https://github.com/AndyObtiva/glimmer) desktop apps (i.e. apps built with [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)) via [Opal](https://opalrb.com/) on [Rails](https://rubyonrails.org/) without changing a line of code. Just insert them as a single require statement in a Rails app, and BOOM! They're running on the web! Apps may then optionally be custom-styled for the web by web designers with standard CSS if needed.
12
12
 
13
13
  Glimmer DSL for Opal successfully reuses the entire [Glimmer](https://github.com/AndyObtiva/glimmer) core DSL engine in [Opal Ruby](https://opalrb.com/) inside a web browser, and as such inherits the full range of powerful Glimmer desktop [data-binding](https://github.com/AndyObtiva/glimmer#data-binding) capabilities for the web.
14
14
 
15
- NOTE: Alpha Version 0.7.0 only supports bare-minimum capabilities for the following [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) [samples](https://github.com/AndyObtiva/glimmer#samples):
15
+ NOTE: Alpha Version 0.7.1 only supports bare-minimum capabilities for the following [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) [samples](https://github.com/AndyObtiva/glimmer#samples):
16
16
 
17
17
  Hello:
18
18
 
@@ -49,16 +49,6 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
49
49
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
50
50
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets)
51
51
 
52
- ## Background
53
-
54
- The original idea behind Glimmer DSL for Opal was that you start by having a [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) desktop app that communicates with a Rails API for any web/cloud concerns. The GUI DSL is very simple in Glimmer DSL for SWT. Once the app is built. You simply embed it in a Rails app as a one line require statement after adding the Glimmer DSL for Opal gem, and BOOM, it just works on the web inside a web browser with the same server/client communication you had in the desktop app (I am working on adding minimal support for net/http in Opal so that desktop apps that use it continue to work in a web browser).
55
-
56
- Part of the idea is that web browsers just render GUI widgets similar to those of a desktop app (after all a web browser is a desktop app), so whether you run your GUI on the desktop or on the web should just be a low-level concern, hopefully automated completely with Glimmer DSL for Opal.
57
-
58
- Last but not least, you would likely want some special branding on the web, so you can push that off to a web designer who would be more than happy to do the web graphic design and customize the look and feel with pure CSS (no need for programming with Ruby or JavaScript). This enables a clean separation of concerns and distribution of tasks among developers and designers, let alone saving effort on the web GUI by reusing the desktop GUI as a base right off the bat.
59
-
60
- Alternatively, web developers may directly use [Glimmer DSL for Opal](https://rubygems.org/gems/glimmer-dsl-opal) to build the GUI of web apps since it is as simple as desktop development, thus requiring a lot less code that is in pure Ruby only (as demonstrated in examples below) and avoiding opaque web concepts like 'render' and 'reactive' due to treating GUI as persistent just like desktop apps do. No HTML/JS/CSS skills are even required. Still, web designers may be involved with CSS only if needed, thanks to the clean semantic markup [Glimmer DSL for Opal](https://rubygems.org/gems/glimmer-dsl-opal) produces.
61
-
62
52
  ## Supported Glimmer DSL Keywords
63
53
 
64
54
  The following keywords from [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) have partial support in Opal:
@@ -99,6 +89,16 @@ Event loop:
99
89
  - `display`
100
90
  - `async_exec`
101
91
 
92
+ ## Background
93
+
94
+ The original idea behind Glimmer DSL for Opal was that you start by having a [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) desktop app that communicates with a Rails API for any web/cloud concerns. The pure Ruby [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) is very simple, so it is more productive to build GUI in it since it does not go through a server/client request/response cycle and can be iterated on locally with a much shorter feedback cycle. Once the GUI and the rest of the app is built. You simply embed it in a Rails app as a one line require statement after adding the Glimmer DSL for Opal gem, and BOOM, it just works on the web inside a web browser with the same server/client communication you had in the desktop app (I am working on adding minimal support for net/http in Opal so that desktop apps that use it continue to work in a web browser. Until then, just use [Opal-jQuery](https://github.com/opal/opal-jquery) http support). That way, you get two apps for one: desktop and web.
95
+
96
+ Part of the idea is that web browsers just render GUI widgets similar to those of a desktop app (after all a web browser is a desktop app), so whether you run your GUI on the desktop or on the web should just be a low-level concern, hopefully automated completely with Glimmer DSL for Opal.
97
+
98
+ Last but not least, you would likely want some special branding on the web, so you can push that off to a web designer who would be more than happy to do the web graphic design and customize the look and feel with pure CSS (no need for programming with Ruby or JavaScript). This enables a clean separation of concerns and distribution of tasks among developers and designers, let alone saving effort on the web GUI by reusing the desktop GUI as a base right off the bat.
99
+
100
+ Alternatively, web developers may directly use [Glimmer DSL for Opal](https://rubygems.org/gems/glimmer-dsl-opal) to build the GUI of web apps since it is as simple as desktop development, thus requiring a lot less code that is in pure Ruby only (as demonstrated in examples below) and avoiding opaque web concepts like 'render' and 'reactive' due to treating GUI as persistent just like desktop apps do. No HTML/JS/CSS skills are even required. Still, web designers may be involved with CSS only if needed, thanks to the clean semantic markup [Glimmer DSL for Opal](https://rubygems.org/gems/glimmer-dsl-opal) automatically produces.
101
+
102
102
  ## Pre-requisites
103
103
 
104
104
  - Rails 5: [https://github.com/rails/rails/tree/5-2-stable](https://github.com/rails/rails/tree/5-2-stable)
@@ -133,7 +133,7 @@ Add the following to `Gemfile`:
133
133
  gem 'opal-rails', '~> 1.1.2'
134
134
  gem 'opal-async', '~> 1.2.0'
135
135
  gem 'opal-jquery', '~> 0.4.4'
136
- gem 'glimmer-dsl-opal', '~> 0.7.0'
136
+ gem 'glimmer-dsl-opal', '~> 0.7.1'
137
137
  gem 'glimmer-dsl-xml', '~> 1.1.0', require: false
138
138
  gem 'glimmer-dsl-css', '~> 1.1.0', require: false
139
139
 
@@ -170,6 +170,8 @@ Edit `app/views/layouts/application.html.erb` and add the following below other
170
170
  <%= stylesheet_link_tag 'glimmer/glimmer', media: 'all', 'data-turbolinks-track': 'reload' %>
171
171
  ```
172
172
 
173
+ Clear the file `app/views/welcomes/index.html.erb` from any content.
174
+
173
175
  Open a `Document.ready?` block and add inside it Glimmer GUI DSL code or a require statement for one of the samples below.
174
176
 
175
177
  ```ruby
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.0
1
+ 0.7.1
@@ -1,3 +1,24 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
1
22
  require 'opal'
2
23
 
3
24
  GLIMMER_DSL_OPAL_ROOT = File.expand_path('../..', __FILE__)
@@ -45,6 +66,7 @@ if RUBY_ENGINE == 'opal'
45
66
  require 'glimmer-dsl-xml'
46
67
  require 'glimmer-dsl-css'
47
68
  Element.alias_native :replace_with, :replaceWith
69
+ Element.alias_native :select
48
70
 
49
71
  # Glimmer::Config.loop_max_count = 20
50
72
 
@@ -54,6 +76,42 @@ if RUBY_ENGINE == 'opal'
54
76
  result ||= method == '<<'
55
77
  result ||= method == 'handle'
56
78
  end
79
+
80
+ class OS
81
+ class << self
82
+ def windows?
83
+ # No Op in Opal
84
+ end
85
+
86
+ def mac?
87
+ # No Op in Opal
88
+ end
89
+
90
+ def linux?
91
+ # No Op in Opal
92
+ end
93
+ end
94
+ end
95
+
96
+ class File
97
+ class << self
98
+ def read(*args, &block)
99
+ # TODO implement via asset downloads in the future
100
+ # No Op in Opal
101
+ end
102
+ end
103
+ end
104
+
105
+ class Display
106
+ class << self
107
+ def setAppName(app_name)
108
+ # No Op in Opal
109
+ end
110
+ def setAppVersion(version)
111
+ # No Op in Opal
112
+ end
113
+ end
114
+ end
57
115
  else
58
116
  require_relative 'glimmer/engine'
59
117
  end
@@ -45,7 +45,7 @@ class Time
45
45
  alias new_original new
46
46
  def new(*args)
47
47
  if args.size >= 7
48
- puts "Dropped timezone #{args[6]} from Time.new(#{args.map(&:to_s)}) constructor arguments since Opal does not support it!"
48
+ Glimmer::Config.logger.debug "Dropped timezone #{args[6]} from Time.new(#{args.map(&:to_s)}) constructor arguments since Opal does not support it!"
49
49
  args = args[0...6]
50
50
  end
51
51
  new_original(*args)
@@ -218,17 +218,17 @@ class HelloTable
218
218
  table_column {
219
219
  text 'Ballpark'
220
220
  width 180
221
- # editor :none
221
+ editor :none
222
222
  }
223
223
  table_column {
224
224
  text 'Home Team'
225
225
  width 150
226
- # editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
226
+ editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
227
227
  }
228
228
  table_column {
229
229
  text 'Away Team'
230
230
  width 150
231
- # editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
231
+ editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
232
232
  }
233
233
  table_column {
234
234
  text 'Promotion'
@@ -1,37 +1,22 @@
1
- class OS
2
- class << self
3
- def windows?
4
- # No Op in Opal
5
- end
6
-
7
- def mac?
8
- # No Op in Opal
9
- end
10
-
11
- def linux?
12
- # No Op in Opal
13
- end
14
- end
15
- end
16
-
17
- class File
18
- class << self
19
- def read(*args, &block)
20
- # TODO implement via asset downloads in the future
21
- # No Op in Opal
22
- end
23
- end
24
- end
25
-
26
- class Display
27
- class << self
28
- def setAppName(app_name)
29
- # No Op in Opal
30
- end
31
- def setAppVersion(version)
32
- # No Op in Opal
33
- end
34
- end
35
- end
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36
21
 
37
22
  require 'glimmer-dsl-opal'
@@ -35,7 +35,7 @@ module Glimmer
35
35
  model_cells = new_model_collection.to_a.map {|m| @table.cells_for(m)}
36
36
  return if table_cells == model_cells
37
37
  if new_model_collection and new_model_collection.is_a?(Array)
38
- # @table_items_observer_registration&.unobserve
38
+ # @table_items_observer_registration&.unobserve # TODO re-enable
39
39
  @table_items_observer_registration = observe(new_model_collection, @column_properties)
40
40
  add_dependent(@table_observer_registration => @table_items_observer_registration)
41
41
  @model_collection = new_model_collection
@@ -45,8 +45,10 @@ module Glimmer
45
45
  end
46
46
 
47
47
  def populate_table(model_collection, parent, column_properties)
48
- return if model_collection&.sort_by(&:hash) == @last_populated_model_collection&.sort_by(&:hash)
48
+ @skip_populate_table = model_collection&.sort_by(&:hash).map {|m| @table.column_properties.map {|p| m.send(p)}} == @last_populated_model_collection_properties
49
+ return if @skip_populate_table
49
50
  @last_populated_model_collection = model_collection
51
+ @last_populated_model_collection_properties = model_collection&.sort_by(&:hash).map {|m| @table.column_properties.map {|p| m.send(p)}}
50
52
  # TODO improve performance
51
53
  selected_table_item_models = parent.selection.map(&:get_data)
52
54
  old_items = parent.items
@@ -20,8 +20,12 @@ module Glimmer
20
20
  end
21
21
 
22
22
  def add_content(parent, &block)
23
- super(parent, &block)
24
- parent.post_add_content
23
+ if parent.rendered?
24
+ super(parent, &block)
25
+ parent.post_add_content
26
+ else
27
+ parent.add_content_on_render(&block)
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -8,7 +8,11 @@ module Glimmer
8
8
 
9
9
  def initialize(parent, args, block)
10
10
  super(parent, args, block)
11
- @layout = GridLayoutProxy.new(self, [])
11
+ @layout = default_layout
12
+ end
13
+
14
+ def default_layout
15
+ GridLayoutProxy.new(self, [])
12
16
  end
13
17
 
14
18
  def dom
@@ -0,0 +1,53 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ module Glimmer
23
+ module SWT
24
+ # Emulates SWT's native org.eclipse.swt.custom.ControlEditor
25
+ class ControlEditor
26
+ ATTRIBUTES = [:grabHorizontal, :grabVertical, :horizontalAlignment, :verticalAlignment, :minimumWidth, :minimumHeight]
27
+ attr_accessor *ATTRIBUTES
28
+ ATTRIBUTES.each do |attribute|
29
+ alias_method attribute.underscore, attribute
30
+ alias_method "#{attribute.underscore}=", "#{attribute}="
31
+ end
32
+
33
+ # TODO consider supporting a java_attr_accessor to get around having to generate all the aliases manually
34
+ attr_accessor :editor
35
+ alias getEditor editor
36
+ alias get_editor editor
37
+ alias setEditor editor=
38
+ alias set_editor editor=
39
+
40
+ # TODO implement `#layout` method if needed
41
+
42
+ attr_reader :composite
43
+
44
+ def initialize(composite)
45
+ @composite = composite
46
+ end
47
+
48
+ # TODO implement showing editor for composite or canvas
49
+ # def editor=(widget)
50
+ # end
51
+ end
52
+ end
53
+ end
@@ -18,7 +18,7 @@ module Glimmer
18
18
  a_layout_class = Glimmer::SWT.const_get(class_name_main.to_sym) rescue Glimmer::SWT.const_get(class_name_alternative.to_sym)
19
19
  a_layout_class if a_layout_class.ancestors.include?(Glimmer::SWT::LayoutProxy)
20
20
  rescue => e
21
- puts "Layout #{keyword} was not found!"
21
+ Glimmer::Config.logger.debug "Layout #{keyword} was not found!"
22
22
  nil
23
23
  end
24
24
 
@@ -74,6 +74,15 @@ module Glimmer
74
74
  dom_element.find('.sort-direction')
75
75
  end
76
76
 
77
+ # Sets editor (e.g. combo)
78
+ def editor=(args)
79
+ @editor = args
80
+ end
81
+
82
+ def editable?
83
+ !@editor&.include?(:none)
84
+ end
85
+
77
86
  def observation_request_to_event_mapping
78
87
  {
79
88
  'on_widget_selected' => {
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/swt/control_editor'
23
+
24
+ module Glimmer
25
+ module SWT
26
+ # Emulates SWT's native org.eclipse.swt.custom.TableEditor
27
+ class TableEditor < ControlEditor
28
+ alias table composite
29
+
30
+ def editor=(editor_widget, table_item, table_column_index)
31
+ # TODO consider making editor not gain an ID or gain a separate set of IDs to avoid clashing with standard widget predictability of ID
32
+ @table_item = table_item
33
+ @table_column_index = table_column_index
34
+ @editor_widget = editor_widget
35
+ @old_value = table_item.cell_dom_element(table_column_index).html
36
+ table_item.cell_dom_element(table_column_index).html('')
37
+ editor_widget.render(table_item.cell_dom_element(table_column_index))
38
+ # TODO tweak the width perfectly so it doesn't expand the table cell
39
+ # editor_widget.dom_element.css('width', 'calc(100% - 20px)')
40
+ editor_widget.dom_element.css('width', '90%') # just a good enough approximation
41
+ editor_widget.dom_element.css('height', "#{minimumHeight}px")
42
+ editor_widget.dom_element.add_class('table-editor')
43
+ # TODO consider relying on autofocus instead
44
+ editor_widget.dom_element.focus
45
+ # TODO consider doing the following line only for :text editor
46
+ editor_widget.dom_element.select
47
+ end
48
+ alias set_editor editor=
49
+ alias setEditor editor=
50
+
51
+ def cancel!
52
+ done!
53
+ end
54
+
55
+ def save!
56
+ done!
57
+ end
58
+
59
+ def done!
60
+ @table_item.cell_dom_element(@table_column_index).html(@old_value) unless @old_value.nil?
61
+ @old_value = nil
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,9 +1,33 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
1
22
  require 'glimmer/swt/widget_proxy'
2
23
 
3
24
  module Glimmer
4
25
  module SWT
5
26
  class TableItemProxy < WidgetProxy
6
27
  STYLE = <<~CSS
28
+ tr.table-item td {
29
+ padding-bottom: 0;
30
+ }
7
31
  tr.table-item:nth-child(even):not(.selected) {
8
32
  background: rgb(243, 244, 246);
9
33
  }
@@ -60,6 +84,10 @@ module Glimmer
60
84
  'tr'
61
85
  end
62
86
 
87
+ def cell_dom_element(column_index)
88
+ dom_element.find("td:nth-child(#{column_index + 1})")
89
+ end
90
+
63
91
  def redraw
64
92
  super() #TODO re-enable and remove below lines
65
93
 
@@ -1,20 +1,242 @@
1
+ # Copyright (c) 2020 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
1
22
  require 'glimmer/swt/widget_proxy'
2
23
  require 'glimmer/swt/table_column_proxy'
24
+ require 'glimmer/swt/table_item_proxy'
25
+ require 'glimmer/swt/table_editor'
3
26
 
4
27
  module Glimmer
5
28
  module SWT
6
- class TableProxy < WidgetProxy
29
+ class TableProxy < CompositeProxy
7
30
  attr_reader :columns, :selection,
8
- :sort_type, :sort_column, :sort_property, :sort_block, :sort_by_block, :additional_sort_properties
31
+ :sort_type, :sort_column, :sort_property, :sort_block, :sort_by_block, :additional_sort_properties,
32
+ :editor, :table_editor
9
33
  attr_accessor :column_properties, :item_count, :data
10
34
  alias items children
11
35
  alias model_binding data
12
36
 
37
+ class << self
38
+ include Glimmer
39
+
40
+ def editors
41
+ @editors ||= {
42
+ # ensure editor can work with string keys not just symbols (leave one string in for testing)
43
+ text: {
44
+ widget_value_property: :text,
45
+ editor_gui: lambda do |args, model, property, table_proxy|
46
+ table_proxy.table_editor.minimumHeight = 10
47
+ table_editor_widget_proxy = text(*args) {
48
+ text model.send(property)
49
+ focus true
50
+ on_focus_lost {
51
+ table_proxy.finish_edit!
52
+ }
53
+ on_key_pressed { |key_event|
54
+ if key_event.keyCode == swt(:cr)
55
+ table_proxy.finish_edit!
56
+ elsif key_event.keyCode == swt(:esc)
57
+ table_proxy.cancel_edit!
58
+ end
59
+ }
60
+ }
61
+ # table_editor_widget_proxy.swt_widget.selectAll # TODO select all
62
+ table_editor_widget_proxy
63
+ end,
64
+ },
65
+ combo: {
66
+ widget_value_property: :text,
67
+ editor_gui: lambda do |args, model, property, table_proxy|
68
+ first_time = true
69
+ table_proxy.table_editor.minimumHeight = 18
70
+ table_editor_widget_proxy = combo(*args) {
71
+ items model.send("#{property}_options")
72
+ text model.send(property)
73
+ focus true
74
+ on_focus_lost {
75
+ table_proxy.finish_edit!
76
+ }
77
+ on_key_pressed { |key_event|
78
+ if key_event.keyCode == swt(:cr)
79
+ table_proxy.finish_edit!
80
+ elsif key_event.keyCode == swt(:esc)
81
+ table_proxy.cancel_edit!
82
+ end
83
+ }
84
+ on_widget_selected {
85
+ if !OS.windows? || !first_time || first_time && model.send(property) != table_editor_widget_proxy.text
86
+ table_proxy.finish_edit!
87
+ end
88
+ }
89
+ }
90
+ table_editor_widget_proxy
91
+ end,
92
+ },
93
+ checkbox: {
94
+ widget_value_property: :selection,
95
+ editor_gui: lambda do |args, model, property, table_proxy|
96
+ first_time = true
97
+ table_proxy.table_editor.minimumHeight = 25
98
+ checkbox(*args) {
99
+ selection model.send(property)
100
+ focus true
101
+ on_widget_selected {
102
+ table_proxy.finish_edit!
103
+ }
104
+ on_focus_lost {
105
+ table_proxy.finish_edit!
106
+ }
107
+ on_key_pressed { |key_event|
108
+ if key_event.keyCode == swt(:cr)
109
+ table_proxy.finish_edit!
110
+ elsif key_event.keyCode == swt(:esc)
111
+ table_proxy.cancel_edit!
112
+ end
113
+ }
114
+ }
115
+ end,
116
+ },
117
+ date: {
118
+ widget_value_property: :date_time,
119
+ editor_gui: lambda do |args, model, property, table_proxy|
120
+ first_time = true
121
+ table_proxy.table_editor.minimumHeight = 25
122
+ date(*args) {
123
+ date_time model.send(property)
124
+ focus true
125
+ on_focus_lost {
126
+ table_proxy.finish_edit!
127
+ }
128
+ on_key_pressed { |key_event|
129
+ if key_event.keyCode == swt(:cr)
130
+ table_proxy.finish_edit!
131
+ elsif key_event.keyCode == swt(:esc)
132
+ table_proxy.cancel_edit!
133
+ end
134
+ }
135
+ }
136
+ end,
137
+ },
138
+ date_drop_down: {
139
+ widget_value_property: :date_time,
140
+ editor_gui: lambda do |args, model, property, table_proxy|
141
+ first_time = true
142
+ table_proxy.table_editor.minimumHeight = 25
143
+ date_drop_down(*args) {
144
+ date_time model.send(property)
145
+ focus true
146
+ on_focus_lost {
147
+ table_proxy.finish_edit!
148
+ }
149
+ on_key_pressed { |key_event|
150
+ if key_event.keyCode == swt(:cr)
151
+ table_proxy.finish_edit!
152
+ elsif key_event.keyCode == swt(:esc)
153
+ table_proxy.cancel_edit!
154
+ end
155
+ }
156
+ }
157
+ end,
158
+ },
159
+ time: {
160
+ widget_value_property: :date_time,
161
+ editor_gui: lambda do |args, model, property, table_proxy|
162
+ first_time = true
163
+ table_proxy.table_editor.minimumHeight = 25
164
+ time(*args) {
165
+ date_time model.send(property)
166
+ focus true
167
+ on_focus_lost {
168
+ table_proxy.finish_edit!
169
+ }
170
+ on_key_pressed { |key_event|
171
+ if key_event.keyCode == swt(:cr)
172
+ table_proxy.finish_edit!
173
+ elsif key_event.keyCode == swt(:esc)
174
+ table_proxy.cancel_edit!
175
+ end
176
+ }
177
+ }
178
+ end,
179
+ },
180
+ radio: {
181
+ widget_value_property: :selection,
182
+ editor_gui: lambda do |args, model, property, table_proxy|
183
+ first_time = true
184
+ table_proxy.table_editor.minimumHeight = 25
185
+ radio(*args) {
186
+ selection model.send(property)
187
+ focus true
188
+ on_widget_selected {
189
+ table_proxy.finish_edit!
190
+ }
191
+ on_focus_lost {
192
+ table_proxy.finish_edit!
193
+ }
194
+ on_key_pressed { |key_event|
195
+ if key_event.keyCode == swt(:cr)
196
+ table_proxy.finish_edit!
197
+ elsif key_event.keyCode == swt(:esc)
198
+ table_proxy.cancel_edit!
199
+ end
200
+ }
201
+ }
202
+ end,
203
+ },
204
+ spinner: {
205
+ widget_value_property: :selection,
206
+ editor_gui: lambda do |args, model, property, table_proxy|
207
+ first_time = true
208
+ table_proxy.table_editor.minimumHeight = 25
209
+ table_editor_widget_proxy = spinner(*args) {
210
+ selection model.send(property)
211
+ focus true
212
+ on_focus_lost {
213
+ table_proxy.finish_edit!
214
+ }
215
+ on_key_pressed { |key_event|
216
+ if key_event.keyCode == swt(:cr)
217
+ table_proxy.finish_edit!
218
+ elsif key_event.keyCode == swt(:esc)
219
+ table_proxy.cancel_edit!
220
+ end
221
+ }
222
+ }
223
+ table_editor_widget_proxy
224
+ end,
225
+ },
226
+ }
227
+ end
228
+ end
229
+
13
230
  def initialize(parent, args, block)
14
231
  super(parent, args, block)
15
232
  @columns = []
16
233
  @children = []
234
+ @editors = []
17
235
  @selection = []
236
+ @table_editor = TableEditor.new(self)
237
+ @table_editor.horizontalAlignment = SWTProxy[:left]
238
+ @table_editor.grabHorizontal = true
239
+ @table_editor.minimumHeight = 20
18
240
  if editable?
19
241
  on_mouse_up { |event|
20
242
  edit_table_item(event.table_item, event.column_index)
@@ -26,10 +248,24 @@ module Glimmer
26
248
  def post_initialize_child(child)
27
249
  if child.is_a?(TableColumnProxy)
28
250
  @columns << child
29
- else
251
+ child.render
252
+ elsif child.is_a?(TableItemProxy)
30
253
  @children << child
254
+ child.render
255
+ else
256
+ @editors << child
257
+ end
258
+ end
259
+
260
+ # Executes for the parent of a child that just got disposed
261
+ def post_dispose_child(child)
262
+ if child.is_a?(TableColumnProxy)
263
+ @columns&.delete(child)
264
+ elsif child.is_a?(TableItemProxy)
265
+ @children&.delete(child)
266
+ else
267
+ @editors&.delete(child)
31
268
  end
32
- child.redraw
33
269
  end
34
270
 
35
271
  def post_add_content
@@ -38,6 +274,10 @@ module Glimmer
38
274
  @initially_sorted = true
39
275
  end
40
276
 
277
+ def default_layout
278
+ nil
279
+ end
280
+
41
281
  def get_data(key=nil)
42
282
  data
43
283
  end
@@ -97,10 +337,6 @@ module Glimmer
97
337
  self.selection = new_selection
98
338
  end
99
339
 
100
- def search(&condition)
101
- items.select {|item| condition.nil? || condition.call(item)}
102
- end
103
-
104
340
  def sort_block=(comparator)
105
341
  @sort_block = comparator
106
342
  end
@@ -223,9 +459,118 @@ module Glimmer
223
459
  @additional_sort_properties = args unless args.empty?
224
460
  end
225
461
 
226
- def edit_table_item(table_item, column_index)
227
- table_item&.edit(column_index) unless column_index.nil?
462
+ def editor=(args)
463
+ @editor = args
464
+ end
465
+
466
+ # Indicates if table is in edit mode, thus displaying a text widget for a table item cell
467
+ def edit_mode?
468
+ !!@edit_mode
469
+ end
470
+
471
+ def cancel_edit!
472
+ @cancel_edit&.call if @edit_mode
473
+ end
474
+
475
+ def finish_edit!
476
+ @finish_edit&.call if @edit_mode
477
+ end
478
+
479
+ # Indicates if table is editing a table item because the user hit ENTER or focused out after making a change in edit mode to a table item cell.
480
+ # It is set to false once change is saved to model
481
+ def edit_in_progress?
482
+ !!@edit_in_progress
483
+ end
484
+
485
+ def edit_selected_table_item(column_index, before_write: nil, after_write: nil, after_cancel: nil)
486
+ edit_table_item(selection.first, column_index, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
487
+ end
488
+
489
+ # TODO migrate the following to the next method
490
+ # def edit_table_item(table_item, column_index)
491
+ # table_item&.edit(column_index) unless column_index.nil?
492
+ # end
493
+
494
+ def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
495
+ return if table_item.nil? || (@edit_mode && @edit_table_item == table_item && @edit_column_index == column_index)
496
+ @edit_column_index = column_index
497
+ @edit_table_item = table_item
498
+ column_index = column_index.to_i
499
+ model = table_item.data
500
+ property = column_properties[column_index]
501
+ cancel_edit!
502
+ return unless columns[column_index].editable?
503
+ action_taken = false
504
+ @edit_mode = true
505
+
506
+ editor_config = columns[column_index].editor || editor
507
+ editor_config = [editor_config].flatten.compact
508
+ editor_widget_options = editor_config.last.is_a?(Hash) ? editor_config.last : {}
509
+ editor_widget_arg_last_index = editor_config.last.is_a?(Hash) ? -2 : -1
510
+ editor_widget = (editor_config[0] || :text).to_sym
511
+ editor_widget_args = editor_config[1..editor_widget_arg_last_index]
512
+ model_editing_property = editor_widget_options[:property] || property
513
+ widget_value_property = TableProxy::editors.symbolize_keys[editor_widget][:widget_value_property]
514
+
515
+ @cancel_edit = lambda do |event=nil|
516
+ @cancel_in_progress = true
517
+ @table_editor.cancel!
518
+ @table_editor_widget_proxy&.dispose
519
+ @table_editor_widget_proxy = nil
520
+ after_cancel&.call
521
+ @edit_in_progress = false
522
+ @cancel_in_progress = false
523
+ @cancel_edit = nil
524
+ @edit_table_item = @edit_column_index = nil if (@edit_mode && @edit_table_item == table_item && @edit_column_index == column_index)
525
+ @edit_mode = false
526
+ end
527
+
528
+ @finish_edit = lambda do |event=nil|
529
+ new_value = @table_editor_widget_proxy&.send(widget_value_property)
530
+ if table_item.disposed?
531
+ @cancel_edit.call
532
+ elsif !new_value.nil? && !action_taken && !@edit_in_progress && !@cancel_in_progress
533
+ action_taken = true
534
+ @edit_in_progress = true
535
+ if new_value == model.send(model_editing_property)
536
+ @cancel_edit.call
537
+ else
538
+ before_write&.call
539
+ @table_editor.save!(widget_value_property: widget_value_property)
540
+ model.send("#{model_editing_property}=", new_value) # makes table update itself, so must search for selected table item again
541
+ # Table refresh happens here because of model update triggering observers, so must retrieve table item again
542
+ edited_table_item = search { |ti| ti.data == model }.first
543
+ show_item(edited_table_item)
544
+ @table_editor_widget_proxy&.dispose
545
+ @table_editor_widget_proxy = nil
546
+ after_write&.call(edited_table_item)
547
+ @edit_in_progress = false
548
+ @edit_table_item = @edit_column_index = nil
549
+ end
550
+ end
551
+ end
552
+
553
+ content {
554
+ @table_editor_widget_proxy = TableProxy::editors.symbolize_keys[editor_widget][:editor_gui].call(editor_widget_args, model, model_editing_property, self)
555
+ }
556
+ @table_editor.set_editor(@table_editor_widget_proxy, table_item, column_index)
557
+ rescue => e
558
+ Glimmer::Config.logger.error {e.full_message}
559
+ raise e
560
+ end
561
+
562
+ def show_item(table_item)
563
+ table_item.dom_element.focus
564
+ end
565
+
566
+ def add_listener(underscored_listener_name, &block)
567
+ enhanced_block = lambda do |event|
568
+ event.extend(TableListenerEvent)
569
+ block.call(event)
570
+ end
571
+ super(underscored_listener_name, &enhanced_block)
228
572
  end
573
+
229
574
 
230
575
  def header_visible=(value)
231
576
  @header_visible = value
@@ -337,7 +682,8 @@ module Glimmer
337
682
  table_id = id
338
683
  table_id_style = css
339
684
  table_id_css_classes = css_classes
340
- table_id_css_classes << 'table'
685
+ table_id_css_classes << 'table' unless table_id_css_classes.include?('table')
686
+ table_id_css_classes << 'editable' if editable? && !table_id_css_classes.include?('editable')
341
687
  table_id_css_classes_string = table_id_css_classes.to_a.join(' ')
342
688
  @dom ||= html {
343
689
  table(id: table_id, style: table_id_style, class: table_id_css_classes_string) {
@@ -29,7 +29,10 @@ module Glimmer
29
29
  include Glimmer
30
30
  include PropertyOwner
31
31
 
32
- attr_reader :parent, :args, :path, :children, :enabled, :foreground, :background, :font, :focus
32
+ attr_reader :parent, :args, :path, :children, :enabled, :foreground, :background, :font, :focus, :disposed?, :rendered
33
+ alias isDisposed disposed?
34
+ alias is_disposed disposed?
35
+ alias rendered? rendered
33
36
 
34
37
  class << self
35
38
  # Factory Method that translates a Glimmer DSL keyword into a WidgetProxy object
@@ -103,6 +106,7 @@ module Glimmer
103
106
  @block = block
104
107
  @children = Set.new # TODO consider moving to composite
105
108
  @enabled = true
109
+ @data = {}
106
110
  DEFAULT_INITIALIZERS[self.class.underscored_widget_name(self)]&.call(self)
107
111
  @parent.post_initialize_child(self) # TODO rename to post_initialize_child to be closer to glimmer-dsl-swt terminology
108
112
  end
@@ -113,17 +117,52 @@ module Glimmer
113
117
  child.render
114
118
  end
115
119
 
120
+ # Executes for the parent of a child that just got disposed
121
+ def post_dispose_child(child)
122
+ @children&.delete(child)
123
+ end
124
+
116
125
  # Executes at the closing of a parent widget curly braces after all children/properties have been added/set
117
126
  def post_add_content
118
127
  # No Op by default
119
128
  end
120
129
 
130
+ def set_data(key=nil, value)
131
+ @data[key] = value
132
+ end
133
+ alias setData set_data
134
+ alias data= set_data
135
+
136
+ def get_data(key=nil)
137
+ @data[key]
138
+ end
139
+ alias getData get_data
140
+ alias data get_data
141
+
121
142
  def css_classes
122
143
  dom_element.attr('class').to_s.split
123
144
  end
124
145
 
125
146
  def dispose
147
+ remove_all_listeners
126
148
  Document.find(path).remove
149
+ parent&.post_dispose_child(self)
150
+ # TODO fire on_widget_disposed listener
151
+ @disposed = true
152
+ end
153
+
154
+ def remove_all_listeners
155
+ observation_request_to_event_mapping.keys.each do |keyword|
156
+ [observation_request_to_event_mapping[keyword]].flatten.each do |mapping|
157
+ @observation_requests[keyword].to_a.each do |event_listener|
158
+ event = mapping[:event]
159
+ event_handler = mapping[:event_handler]
160
+ event_element_css_selector = mapping[:event_element_css_selector]
161
+ the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
162
+ the_listener_dom_element.off(event)
163
+ end
164
+ end
165
+ end
127
166
  end
128
167
 
129
168
  def path
@@ -172,12 +211,17 @@ module Glimmer
172
211
  @parent.path
173
212
  end
174
213
 
175
- def render
214
+ def parent_dom_element
215
+ Document.find(parent_path)
216
+ end
217
+
218
+ def render(custom_parent_dom_element = nil)
219
+ the_parent_dom_element = custom_parent_dom_element || parent_dom_element
176
220
  old_element = dom_element
177
221
  brand_new = @dom.nil? || old_element.empty?
178
- build_dom
222
+ build_dom(!custom_parent_dom_element) # TODO handle custom parent layout by passing parent instead of parent dom element
179
223
  if brand_new
180
- Document.find(parent_path).append(@dom)
224
+ the_parent_dom_element.append(@dom)
181
225
  else
182
226
  old_element.replace_with(@dom)
183
227
  end
@@ -190,13 +234,29 @@ module Glimmer
190
234
  children.each do |child|
191
235
  child.render
192
236
  end
237
+ @rendered = true
238
+ content_on_render_blocks.each { |content_block| content(&content_block) }
193
239
  end
194
240
  alias redraw render
195
241
 
196
- def build_dom
242
+ def content_on_render_blocks
243
+ @content_on_render_blocks ||= []
244
+ end
245
+
246
+ def add_content_on_render(&content_block)
247
+ if rendered?
248
+ content_block.call
249
+ else
250
+ content_on_render_blocks << content_block
251
+ end
252
+ end
253
+
254
+ def build_dom(layout=true)
255
+ # TODO consider passing parent element instead and having table item include a table cell widget only for opal
197
256
  @dom = nil
198
257
  @dom = dom
199
258
  @dom = @parent.layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.layout
259
+ @dom
200
260
  end
201
261
 
202
262
  def content(&block)
@@ -271,10 +331,6 @@ module Glimmer
271
331
  element
272
332
  end
273
333
 
274
- def parent_dom_element
275
- Document.find(parent_path)
276
- end
277
-
278
334
  def listener_path
279
335
  path
280
336
  end
@@ -334,6 +390,14 @@ module Glimmer
334
390
  super(attribute_name, *args) # PropertyOwner
335
391
  end
336
392
 
393
+ def method_missing(method, *args, &block)
394
+ if method.to_s.start_with?('on_')
395
+ handle_observation_request(method, &block)
396
+ else
397
+ super(method, *args, &block)
398
+ end
399
+ end
400
+
337
401
  def apply_property_type_converters(attribute_name, args)
338
402
  if args.count == 1
339
403
  value = args.first
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-opal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - AndyMaleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-02 00:00:00.000000000 Z
11
+ date: 2020-12-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -311,6 +311,7 @@ files:
311
311
  - lib/glimmer/swt/color_proxy.rb
312
312
  - lib/glimmer/swt/combo_proxy.rb
313
313
  - lib/glimmer/swt/composite_proxy.rb
314
+ - lib/glimmer/swt/control_editor.rb
314
315
  - lib/glimmer/swt/custom/checkbox_group.rb
315
316
  - lib/glimmer/swt/custom/radio_group.rb
316
317
  - lib/glimmer/swt/date_time_proxy.rb
@@ -338,6 +339,7 @@ files:
338
339
  - lib/glimmer/swt/tab_folder_proxy.rb
339
340
  - lib/glimmer/swt/tab_item_proxy.rb
340
341
  - lib/glimmer/swt/table_column_proxy.rb
342
+ - lib/glimmer/swt/table_editor.rb
341
343
  - lib/glimmer/swt/table_item_proxy.rb
342
344
  - lib/glimmer/swt/table_proxy.rb
343
345
  - lib/glimmer/swt/text_proxy.rb