glimmer-dsl-opal 0.6.0 → 0.7.3

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -0
  3. data/README.md +428 -18
  4. data/VERSION +1 -1
  5. data/app/assets/stylesheets/{glimmer.css → glimmer/glimmer.css} +1 -1
  6. data/lib/display.rb +31 -0
  7. data/lib/file.rb +29 -0
  8. data/lib/glimmer-dsl-opal.rb +33 -5
  9. data/lib/glimmer-dsl-opal/ext/date.rb +11 -0
  10. data/lib/glimmer-dsl-opal/ext/struct.rb +37 -0
  11. data/lib/glimmer-dsl-opal/samples/elaborate/tic_tac_toe.rb +23 -0
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +283 -0
  13. data/lib/glimmer-dsl-swt.rb +20 -35
  14. data/lib/glimmer/data_binding/table_items_binding.rb +32 -19
  15. data/lib/glimmer/dsl/opal/block_property_expression.rb +41 -0
  16. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +1 -1
  17. data/lib/glimmer/dsl/opal/dsl.rb +2 -0
  18. data/lib/glimmer/dsl/opal/widget_expression.rb +7 -3
  19. data/lib/glimmer/engine.rb +1 -1
  20. data/lib/glimmer/swt/button_proxy.rb +5 -5
  21. data/lib/glimmer/swt/color_proxy.rb +45 -45
  22. data/lib/glimmer/swt/combo_proxy.rb +42 -3
  23. data/lib/glimmer/swt/composite_proxy.rb +7 -3
  24. data/lib/glimmer/swt/control_editor.rb +54 -0
  25. data/lib/glimmer/swt/date_time_proxy.rb +71 -5
  26. data/lib/glimmer/swt/display_proxy.rb +6 -2
  27. data/lib/glimmer/swt/fill_layout_proxy.rb +1 -1
  28. data/lib/glimmer/swt/font_proxy.rb +4 -4
  29. data/lib/glimmer/swt/label_proxy.rb +2 -2
  30. data/lib/glimmer/swt/layout_data_proxy.rb +13 -10
  31. data/lib/glimmer/swt/layout_proxy.rb +5 -5
  32. data/lib/glimmer/swt/list_proxy.rb +2 -2
  33. data/lib/glimmer/swt/message_box_proxy.rb +4 -2
  34. data/lib/glimmer/swt/property_owner.rb +2 -2
  35. data/lib/glimmer/swt/shell_proxy.rb +8 -0
  36. data/lib/glimmer/swt/tab_folder_proxy.rb +2 -2
  37. data/lib/glimmer/swt/tab_item_proxy.rb +7 -7
  38. data/lib/glimmer/swt/table_column_proxy.rb +71 -12
  39. data/lib/glimmer/swt/table_editor.rb +65 -0
  40. data/lib/glimmer/swt/table_item_proxy.rb +50 -7
  41. data/lib/glimmer/swt/table_proxy.rb +581 -14
  42. data/lib/glimmer/swt/text_proxy.rb +49 -1
  43. data/lib/glimmer/swt/widget_proxy.rb +120 -22
  44. data/lib/glimmer/ui/custom_widget.rb +8 -8
  45. data/lib/net/http.rb +1 -5
  46. data/lib/os.rb +36 -0
  47. data/lib/uri.rb +3 -3
  48. metadata +31 -10
  49. data/lib/glimmer/data_binding/ext/observable_model.rb +0 -40
@@ -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'
@@ -12,26 +12,32 @@ module Glimmer
12
12
  include DataBinding::Observer
13
13
 
14
14
  def initialize(parent, model_binding, column_properties)
15
- @last_model_collection = nil
15
+ @last_populated_model_collection = nil
16
16
  @table = parent
17
17
  @model_binding = model_binding
18
18
  @column_properties = column_properties
19
- if @table.respond_to?(:column_properties=)
20
- @table.column_properties = @column_properties
21
- ##else # assume custom widget
22
- ## @table.body_root.column_properties = @column_properties
23
- end
24
- call(@model_binding.evaluate_property)
25
- model = model_binding.base_model
26
- observe(model, model_binding.property_name_expression)
19
+ @table.data = @model_binding
27
20
  ##@table.on_widget_disposed do |dispose_event| # doesn't seem needed within Opal
28
21
  ## unregister_all_observables
29
22
  ##end
23
+ if @table.respond_to?(:column_properties=)
24
+ @table.column_properties = @column_properties
25
+ else # assume custom widget
26
+ @table.body_root.column_properties = @column_properties
27
+ end
28
+ @table_observer_registration = observe(model_binding)
29
+ call
30
30
  end
31
31
 
32
32
  def call(new_model_collection=nil)
33
+ new_model_collection = @model_binding.evaluate_property # this ensures applying converters (e.g. :on_read)
34
+ table_cells = @table.items.map {|item| @table.column_properties.size.times.map {|i| item.get_text(i)} }
35
+ model_cells = new_model_collection.to_a.map {|m| @table.cells_for(m)}
36
+ return if table_cells == model_cells
33
37
  if new_model_collection and new_model_collection.is_a?(Array)
34
- observe(new_model_collection, @column_properties)
38
+ @table_items_observer_registration&.unobserve
39
+ @table_items_observer_registration = observe(new_model_collection, @column_properties)
40
+ add_dependent(@table_observer_registration => @table_items_observer_registration)
35
41
  @model_collection = new_model_collection
36
42
  end
37
43
  populate_table(@model_collection, @table, @column_properties)
@@ -39,8 +45,10 @@ module Glimmer
39
45
  end
40
46
 
41
47
  def populate_table(model_collection, parent, column_properties)
42
- return if model_collection&.sort_by(&:hash) == @last_model_collection&.sort_by(&:hash)
43
- @last_model_collection = model_collection
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
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)}}
44
52
  # TODO improve performance
45
53
  selected_table_item_models = parent.selection.map(&:get_data)
46
54
  old_items = parent.items
@@ -54,17 +62,22 @@ module Glimmer
54
62
  table_item.set_data(model)
55
63
  table_item.id = old_item_ids_per_model[model.hash] if old_item_ids_per_model[model.hash]
56
64
  end
57
- selected_table_items = parent.search {|item| selected_table_item_models.include?(item.get_data) }
58
- selected_table_items = [parent.items.first] if selected_table_items.empty? && !parent.items.empty?
59
- parent.selection = selected_table_items unless selected_table_items.empty?
65
+ parent.selection = parent.search {|item| selected_table_item_models.include?(item.get_data) }
60
66
  parent.redraw
61
67
  end
62
68
 
63
69
  def sort_table(model_collection, parent, column_properties)
64
- return if model_collection == @last_model_collection
65
- parent.items = parent.items.sort_by { |item| model_collection.index(item.get_data) }
66
- @last_model_collection = model_collection
67
- end
70
+ return if model_collection == @last_sorted_model_collection
71
+ if model_collection == @last_populated_model_collection
72
+ # Reapply the last table sort. The model collection has just been populated since it diverged from what it was before
73
+ # TODO optimize in the future by sorting elements in DOM directly
74
+ parent.sort!
75
+ else
76
+ # The model collection was sorted by the model, but beyond sorting, it did not change from the last populated model collection.
77
+ parent.items = parent.items.sort_by { |item| model_collection.index(item.get_data) }
78
+ @last_sorted_model_collection = @last_populated_model_collection = model_collection
79
+ end
80
+ end
68
81
  end
69
82
  end
70
83
  end
@@ -0,0 +1,41 @@
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
+
24
+ module Glimmer
25
+ module DSL
26
+ module Opal
27
+ class BlockPropertyExpression < Expression
28
+ def can_interpret?(parent, keyword, *args, &block)
29
+ block_given? and
30
+ args.size == 0 and
31
+ parent.respond_to?("#{keyword}_block=")
32
+ end
33
+
34
+ def interpret(parent, keyword, *args, &block)
35
+ parent.send("#{keyword}_block=", block)
36
+ nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -44,7 +44,7 @@ module Glimmer
44
44
  def interpret(parent, keyword, *args, &block)
45
45
  custom_widget_class = UI::CustomWidget.for(keyword)
46
46
  # TODO clean code by extracting methods into CustomShell
47
- if !Glimmer::UI::CustomShell.requested? && custom_widget_class.ancestors.include?(Glimmer::UI::CustomShell)
47
+ if !Glimmer::UI::CustomShell.requested? && custom_widget_class&.ancestors&.to_a.include?(Glimmer::UI::CustomShell)
48
48
  if Glimmer::SWT::DisplayProxy.instance.shells.empty?
49
49
  custom_widget_class.new(parent, *args, {}, &block)
50
50
  else
@@ -25,6 +25,7 @@ require 'glimmer/dsl/opal/custom_widget_expression'
25
25
  require 'glimmer/dsl/opal/swt_expression'
26
26
  require 'glimmer/dsl/opal/radio_group_selection_data_binding_expression'
27
27
  require 'glimmer/dsl/opal/checkbox_group_selection_data_binding_expression'
28
+ require 'glimmer/dsl/opal/block_property_expression'
28
29
 
29
30
  module Glimmer
30
31
  module DSL
@@ -42,6 +43,7 @@ module Glimmer
42
43
  data_binding
43
44
  font
44
45
  layout
46
+ block_property
45
47
  property
46
48
  widget
47
49
  ]
@@ -16,12 +16,16 @@ module Glimmer
16
16
  end
17
17
 
18
18
  def interpret(parent, keyword, *args, &block)
19
- Glimmer::SWT::WidgetProxy.for(keyword, parent, args)
19
+ Glimmer::SWT::WidgetProxy.for(keyword, parent, args, block)
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
@@ -3,7 +3,7 @@ module Glimmer
3
3
  isolate_namespace Glimmer
4
4
 
5
5
  initializer "glimmer.assets.precompile" do |app|
6
- app.config.assets.precompile += %w( glimmer.css jquery-ui.css jquery-ui.structure.css jquery-ui.theme.css jquery.ui.timepicker.css )
6
+ app.config.assets.precompile += %w( glimmer/glimmer.css )
7
7
  end
8
8
  end
9
9
  end
@@ -6,13 +6,13 @@ module Glimmer
6
6
  module SWT
7
7
  class ButtonProxy < WidgetProxy
8
8
  class << self
9
- def create(keyword, parent, args)
9
+ def create(keyword, parent, args, block)
10
10
  if args.to_a.include?(:radio)
11
- RadioProxy.new(parent, args)
11
+ RadioProxy.new(parent, args, block)
12
12
  elsif args.to_a.include?(:check)
13
- CheckboxProxy.new(parent, args)
13
+ CheckboxProxy.new(parent, args, block)
14
14
  else
15
- new(parent, args)
15
+ new(parent, args, block)
16
16
  end
17
17
  end
18
18
  end
@@ -44,7 +44,7 @@ module Glimmer
44
44
  input_disabled = @enabled ? {} : {'disabled': 'disabled'}
45
45
  input_args = input_args.merge(type: 'password') if has_style?(:password)
46
46
  @dom ||= html {
47
- button(input_args.merge(id: input_id, class: name, style: input_style, style: 'min-width: 27px; min-height: 27px;').merge(input_disabled)) {
47
+ button(input_args.merge(id: input_id, class: name, style: input_style, style: 'min-width: 32px; min-height: 32px;').merge(input_disabled)) {
48
48
  input_text.to_s == '' ? '&nbsp;' : input_text
49
49
  }
50
50
  }.to_s
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2020 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -28,44 +28,44 @@ module Glimmer
28
28
  # Follows the Proxy Design Pattern
29
29
  class ColorProxy
30
30
  SWT_COLOR_TRANSLATION = {
31
- "widget_foreground" => [0, 0, 0],
32
- "blue" => [0, 0, 255],
33
- "widget_dark_shadow" => [0, 0, 0],
34
- "title_foreground" => [0, 0, 0],
35
- "yellow" => [255, 255, 0],
36
- "widget_highlight_shadow" => [255, 255, 255],
37
- "dark_cyan" => [0, 128, 128],
38
- "list_foreground" => [0, 0, 0],
39
- "dark_blue" => [0, 0, 128],
40
- "dark_yellow" => [128, 128, 0],
41
- "cyan" => [0, 255, 255],
42
- "info_background" => [236, 235, 236],
43
- "link_foreground" => [0, 104, 218],
44
- "title_inactive_foreground" => [0, 0, 0],
45
- "title_background_gradient" => [179, 215, 255],
46
- "red" => [255, 0, 0],
47
- "title_inactive_background_gradient" => [220, 220, 220],
48
- "transparent" => [255, 255, 255],
49
- "widget_light_shadow" => [232, 232, 232],
50
- "dark_magenta" => [128, 0, 128],
51
- "white" => [255, 255, 255],
52
- "list_selection" => [179, 215, 255],
53
- "gray" => [192, 192, 192],
54
- "widget_border" => [0, 0, 0],
55
- "widget_background" => [236, 236, 236],
56
- "info_foreground" => [0, 0, 0],
57
- "title_inactive_background" => [220, 220, 220],
58
- "widget_disabled_foreground" => [220, 220, 220],
59
- "list_background" => [255, 255, 255],
60
- "magenta" => [255, 0, 255],
61
- "title_background" => [0, 99, 225],
62
- "text_disabled_background" => [255, 255, 255],
63
- "black" => [0, 0, 0],
64
- "dark_gray" => [128, 128, 128],
65
- "list_selection_text" => [0, 0, 0],
66
- "dark_red" => [128, 0, 0],
67
- "widget_normal_shadow" => [159, 159, 159],
68
- "dark_green" => [0, 128, 0],
31
+ "widget_foreground" => [0, 0, 0],
32
+ "blue" => [0, 0, 255],
33
+ "widget_dark_shadow" => [0, 0, 0],
34
+ "title_foreground" => [0, 0, 0],
35
+ "yellow" => [255, 255, 0],
36
+ "widget_highlight_shadow" => [255, 255, 255],
37
+ "dark_cyan" => [0, 128, 128],
38
+ "list_foreground" => [0, 0, 0],
39
+ "dark_blue" => [0, 0, 128],
40
+ "dark_yellow" => [128, 128, 0],
41
+ "cyan" => [0, 255, 255],
42
+ "info_background" => [236, 235, 236],
43
+ "link_foreground" => [0, 104, 218],
44
+ "title_inactive_foreground" => [0, 0, 0],
45
+ "title_background_gradient" => [179, 215, 255],
46
+ "red" => [255, 0, 0],
47
+ "title_inactive_background_gradient" => [220, 220, 220],
48
+ "transparent" => [255, 255, 255],
49
+ "widget_light_shadow" => [232, 232, 232],
50
+ "dark_magenta" => [128, 0, 128],
51
+ "white" => [255, 255, 255],
52
+ "list_selection" => [179, 215, 255],
53
+ "gray" => [192, 192, 192],
54
+ "widget_border" => [0, 0, 0],
55
+ "widget_background" => [236, 236, 236],
56
+ "info_foreground" => [0, 0, 0],
57
+ "title_inactive_background" => [220, 220, 220],
58
+ "widget_disabled_foreground" => [220, 220, 220],
59
+ "list_background" => [255, 255, 255],
60
+ "magenta" => [255, 0, 255],
61
+ "title_background" => [0, 99, 225],
62
+ "text_disabled_background" => [255, 255, 255],
63
+ "black" => [0, 0, 0],
64
+ "dark_gray" => [128, 128, 128],
65
+ "list_selection_text" => [0, 0, 0],
66
+ "dark_red" => [128, 0, 0],
67
+ "widget_normal_shadow" => [159, 159, 159],
68
+ "dark_green" => [0, 128, 0],
69
69
  "green" => [0, 255, 0]
70
70
  }
71
71
 
@@ -87,19 +87,19 @@ module Glimmer
87
87
  # rgba is 4 arguments representing Red, Green, Blue, and Alpha numeric values
88
88
  #
89
89
  def initialize(*args)
90
- @args = args
90
+ @args = args
91
91
  case @args.size
92
92
  when 1
93
93
  @alpha = nil
94
- if @args.first.is_a?(String) || @args.first.is_a?(Symbol)
95
- standard_color = @args.first.to_s.downcase.sub('COLOR_', '')
94
+ if @args.first.is_a?(String) || @args.first.is_a?(Symbol)
95
+ standard_color = @args.first.to_s.downcase.sub('COLOR_', '')
96
96
  @red, @green, @blue = SWT_COLOR_TRANSLATION[standard_color]
97
97
  else
98
98
  @red, @green, @blue = [0, 0, 0]
99
99
  end
100
100
  when 3..4
101
101
  @red, @green, @blue, @alpha = @args
102
- end
102
+ end
103
103
  end
104
104
 
105
105
  def to_css
@@ -7,8 +7,8 @@ module Glimmer
7
7
  include Glimmer::DataBinding::ObservableElement
8
8
  attr_reader :text, :items
9
9
 
10
- def initialize(parent, args)
11
- super(parent, args)
10
+ def initialize(parent, args, block)
11
+ super(parent, args, block)
12
12
  @items = []
13
13
  end
14
14
 
@@ -45,7 +45,46 @@ module Glimmer
45
45
  event_listener.call(event)
46
46
  }
47
47
  }
48
- }
48
+ },
49
+ 'on_key_pressed' => {
50
+ event: 'keydown',
51
+ event_handler: -> (event_listener) {
52
+ -> (event) {
53
+ @last_key_pressed_event = event
54
+ @text = event.target.value
55
+ # TODO generalize this solution to all widgets that support key presses
56
+ # TODO support event.location once DOM3 is supported by opal-jquery
57
+ event.define_singleton_method(:keyCode) {event.which}
58
+ event.define_singleton_method(:key_code, &event.method(:keyCode))
59
+ event.define_singleton_method(:character) {event.which.chr}
60
+ event.define_singleton_method(:stateMask) do
61
+ state_mask = 0
62
+ state_mask |= SWTProxy[:alt] if event.alt_key
63
+ state_mask |= SWTProxy[:ctrl] if event.ctrl_key
64
+ state_mask |= SWTProxy[:shift] if event.shift_key
65
+ state_mask |= SWTProxy[:command] if event.meta_key
66
+ state_mask
67
+ end
68
+ event.define_singleton_method(:state_mask, &event.method(:stateMask))
69
+ doit = true
70
+ event.define_singleton_method(:doit=) do |value|
71
+ doit = value
72
+ end
73
+ event.define_singleton_method(:doit) { doit }
74
+ event_listener.call(event)
75
+
76
+ # TODO Fix doit false, it's not stopping input
77
+ unless doit
78
+ event.prevent
79
+ event.prevent_default
80
+ event.stop_propagation
81
+ event.stop_immediate_propagation
82
+ end
83
+
84
+ doit
85
+ }
86
+ }
87
+ },
49
88
  }
50
89
  end
51
90