glimmer-dsl-opal 0.0.9 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +999 -177
  4. data/VERSION +1 -1
  5. data/lib/glimmer-dsl-opal.rb +49 -10
  6. data/lib/glimmer-dsl-opal/ext/date.rb +13 -0
  7. data/lib/glimmer-dsl-opal/ext/exception.rb +5 -0
  8. data/lib/{samples → glimmer-dsl-opal/samples}/elaborate/contact_manager.rb +1 -1
  9. data/lib/{samples → glimmer-dsl-opal/samples}/elaborate/contact_manager/contact.rb +0 -0
  10. data/lib/{samples → glimmer-dsl-opal/samples}/elaborate/contact_manager/contact_manager_presenter.rb +0 -0
  11. data/lib/{samples → glimmer-dsl-opal/samples}/elaborate/contact_manager/contact_repository.rb +24 -99
  12. data/lib/{samples → glimmer-dsl-opal/samples}/elaborate/login.rb +0 -0
  13. data/lib/{samples → glimmer-dsl-opal/samples}/elaborate/tic_tac_toe.rb +0 -0
  14. data/lib/{samples → glimmer-dsl-opal/samples}/elaborate/tic_tac_toe/board.rb +0 -0
  15. data/lib/{samples → glimmer-dsl-opal/samples}/elaborate/tic_tac_toe/cell.rb +0 -0
  16. data/lib/{samples → glimmer-dsl-opal/samples}/hello/hello_browser.rb +0 -0
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox.rb +85 -0
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox_group.rb +68 -0
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +63 -0
  20. data/lib/{samples → glimmer-dsl-opal/samples}/hello/hello_computed.rb +19 -19
  21. data/lib/{samples → glimmer-dsl-opal/samples}/hello/hello_computed/contact.rb +0 -0
  22. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +155 -0
  23. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_widget.rb +86 -0
  24. data/lib/glimmer-dsl-opal/samples/hello/hello_group.rb +104 -0
  25. data/lib/{samples → glimmer-dsl-opal/samples}/hello/hello_list_multi_selection.rb +0 -0
  26. data/lib/{samples → glimmer-dsl-opal/samples}/hello/hello_list_single_selection.rb +0 -0
  27. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +108 -0
  28. data/lib/glimmer-dsl-opal/samples/hello/hello_radio_group.rb +84 -0
  29. data/lib/glimmer-dsl-opal/samples/hello/hello_tab.rb +50 -0
  30. data/lib/glimmer-dsl-opal/samples/hello/hello_world.rb +29 -0
  31. data/lib/glimmer-dsl-opal/vendor/jquery.js +2 -0
  32. data/lib/glimmer-dsl-swt.rb +37 -0
  33. data/lib/glimmer/data_binding/element_binding.rb +1 -0
  34. data/lib/glimmer/data_binding/ext/observable_model.rb +5 -5
  35. data/lib/glimmer/data_binding/list_selection_binding.rb +1 -1
  36. data/lib/glimmer/data_binding/table_items_binding.rb +3 -3
  37. data/lib/glimmer/dsl/opal/async_exec_expression.rb +23 -7
  38. data/lib/glimmer/dsl/opal/checkbox_group_selection_data_binding_expression.rb +61 -0
  39. data/lib/glimmer/dsl/opal/color_expression.rb +38 -0
  40. data/lib/glimmer/dsl/opal/column_properties_expression.rb +2 -2
  41. data/lib/glimmer/dsl/opal/combo_selection_data_binding_expression.rb +2 -2
  42. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +94 -0
  43. data/lib/glimmer/dsl/opal/display_expression.rb +40 -0
  44. data/lib/glimmer/dsl/opal/dsl.rb +18 -16
  45. data/lib/glimmer/dsl/opal/exec_expression.rb +55 -0
  46. data/lib/glimmer/dsl/opal/font_expression.rb +47 -0
  47. data/lib/glimmer/dsl/opal/layout_data_expression.rb +2 -2
  48. data/lib/glimmer/dsl/opal/layout_expression.rb +22 -0
  49. data/lib/glimmer/dsl/opal/list_selection_data_binding_expression.rb +2 -3
  50. data/lib/glimmer/dsl/opal/message_box_expression.rb +2 -2
  51. data/lib/glimmer/dsl/opal/property_expression.rb +6 -2
  52. data/lib/glimmer/dsl/opal/radio_group_selection_data_binding_expression.rb +61 -0
  53. data/lib/glimmer/dsl/opal/rgb_expression.rb +32 -0
  54. data/lib/glimmer/dsl/opal/rgba_expression.rb +32 -0
  55. data/lib/glimmer/dsl/opal/shell_expression.rb +20 -3
  56. data/lib/glimmer/dsl/opal/swt_expression.rb +46 -0
  57. data/lib/glimmer/dsl/opal/sync_exec_expression.rb +33 -0
  58. data/lib/glimmer/dsl/opal/table_expression.rb +2 -2
  59. data/lib/glimmer/dsl/opal/table_items_data_binding_expression.rb +2 -2
  60. data/lib/glimmer/dsl/opal/widget_expression.rb +24 -0
  61. data/lib/glimmer/dsl/opal/widget_listener_expression.rb +16 -3
  62. data/lib/glimmer/swt.rb +499 -0
  63. data/lib/glimmer/swt/browser_proxy.rb +27 -0
  64. data/lib/glimmer/swt/button_proxy.rb +54 -0
  65. data/lib/glimmer/swt/checkbox_proxy.rb +80 -0
  66. data/lib/glimmer/swt/color_proxy.rb +119 -0
  67. data/lib/glimmer/{opal/select_proxy.rb → swt/combo_proxy.rb} +23 -18
  68. data/lib/glimmer/swt/composite_proxy.rb +31 -0
  69. data/lib/glimmer/swt/custom/checkbox_group.rb +142 -0
  70. data/lib/glimmer/swt/custom/radio_group.rb +143 -0
  71. data/lib/glimmer/swt/display_proxy.rb +79 -0
  72. data/lib/glimmer/{opal → swt}/event_listener_proxy.rb +1 -1
  73. data/lib/glimmer/swt/fill_layout_proxy.rb +84 -0
  74. data/lib/glimmer/swt/font_proxy.rb +79 -0
  75. data/lib/glimmer/swt/grid_layout_proxy.rb +82 -0
  76. data/lib/glimmer/swt/group_proxy.rb +38 -0
  77. data/lib/glimmer/swt/label_proxy.rb +54 -0
  78. data/lib/glimmer/swt/layout_data_proxy.rb +105 -0
  79. data/lib/glimmer/swt/layout_proxy.rb +63 -0
  80. data/lib/glimmer/{opal → swt}/list_proxy.rb +30 -22
  81. data/lib/glimmer/swt/make_shift_shell_proxy.rb +38 -0
  82. data/lib/glimmer/swt/message_box_proxy.rb +143 -0
  83. data/lib/glimmer/{opal → swt}/point.rb +1 -1
  84. data/lib/glimmer/{opal → swt}/property_owner.rb +1 -1
  85. data/lib/glimmer/swt/radio_proxy.rb +81 -0
  86. data/lib/glimmer/swt/row_layout_proxy.rb +128 -0
  87. data/lib/glimmer/swt/scrolled_composite_proxy.rb +20 -0
  88. data/lib/glimmer/swt/shell_proxy.rb +260 -0
  89. data/lib/glimmer/swt/style_constantizable.rb +154 -0
  90. data/lib/glimmer/swt/styled_text_proxy.rb +44 -0
  91. data/lib/glimmer/swt/swt_proxy.rb +53 -0
  92. data/lib/glimmer/{opal/tab_folder.rb → swt/tab_folder_proxy.rb} +20 -21
  93. data/lib/glimmer/swt/tab_item_proxy.rb +84 -0
  94. data/lib/glimmer/{opal/table_column.rb → swt/table_column_proxy.rb} +13 -7
  95. data/lib/glimmer/{opal/table_item.rb → swt/table_item_proxy.rb} +59 -48
  96. data/lib/glimmer/{opal → swt}/table_proxy.rb +66 -56
  97. data/lib/glimmer/swt/text_proxy.rb +46 -0
  98. data/lib/glimmer/swt/widget_proxy.rb +511 -0
  99. data/lib/glimmer/ui/custom_shell.rb +92 -0
  100. data/lib/glimmer/ui/custom_widget.rb +292 -0
  101. data/lib/glimmer/util/proc_tracker.rb +39 -0
  102. data/lib/net/http.rb +17 -0
  103. data/lib/uri.rb +64 -0
  104. metadata +142 -67
  105. data/lib/glimmer/dsl/opal/browser_expression.rb +0 -17
  106. data/lib/glimmer/dsl/opal/button_expression.rb +0 -18
  107. data/lib/glimmer/dsl/opal/combo_expression.rb +0 -17
  108. data/lib/glimmer/dsl/opal/composite_expression.rb +0 -17
  109. data/lib/glimmer/dsl/opal/grid_layout_expression.rb +0 -17
  110. data/lib/glimmer/dsl/opal/label_expression.rb +0 -17
  111. data/lib/glimmer/dsl/opal/list_expression.rb +0 -17
  112. data/lib/glimmer/dsl/opal/tab_folder_expression.rb +0 -17
  113. data/lib/glimmer/dsl/opal/tab_item_expression.rb +0 -17
  114. data/lib/glimmer/dsl/opal/text_expression.rb +0 -22
  115. data/lib/glimmer/opal/display_proxy.rb +0 -23
  116. data/lib/glimmer/opal/div_proxy.rb +0 -29
  117. data/lib/glimmer/opal/document_proxy.rb +0 -187
  118. data/lib/glimmer/opal/element_proxy.rb +0 -304
  119. data/lib/glimmer/opal/grid_layout_proxy.rb +0 -54
  120. data/lib/glimmer/opal/iframe_proxy.rb +0 -23
  121. data/lib/glimmer/opal/input_proxy.rb +0 -45
  122. data/lib/glimmer/opal/label_proxy.rb +0 -25
  123. data/lib/glimmer/opal/layout_data_proxy.rb +0 -52
  124. data/lib/glimmer/opal/modal.rb +0 -94
  125. data/lib/glimmer/opal/tab_item.rb +0 -98
  126. data/lib/samples/elaborate/launch +0 -6
  127. data/lib/samples/hello/hello_combo.rb +0 -34
  128. data/lib/samples/hello/hello_tab.rb +0 -24
  129. data/lib/samples/hello/hello_world.rb +0 -8
  130. data/lib/samples/hello/launch +0 -10
  131. data/lib/samples/launch +0 -4
@@ -1,8 +1,8 @@
1
- require 'glimmer/opal/element_proxy'
1
+ require 'glimmer/swt/widget_proxy'
2
2
 
3
3
  module Glimmer
4
- module Opal
5
- class TableItem < ElementProxy
4
+ module SWT
5
+ class TableItemProxy < WidgetProxy
6
6
  attr_reader :data
7
7
 
8
8
  def initialize(parent, args)
@@ -45,10 +45,58 @@ module Glimmer
45
45
  @data_hash ||= {}
46
46
  end
47
47
 
48
- def name
48
+ def parent_path
49
+ parent.items_path
50
+ end
51
+
52
+ def element
49
53
  'tr'
50
54
  end
51
55
 
56
+ def redraw
57
+ super() #TODO re-enalbe and remove below lines
58
+
59
+ # TODO perhaps turn the following lambdas into methods
60
+ table_item_edit_handler = lambda do |event, cancel = false|
61
+ Async::Task.new do
62
+ text_value = event.target.value
63
+ edit_property = parent.column_properties[@edit_column_index]
64
+ edit_model = get_data
65
+ if !cancel && edit_model.send(edit_property) != text_value
66
+ edit_model.send("#{edit_property}=", text_value)
67
+ set_text(@edit_column_index, text_value)
68
+ end
69
+ @edit_column_index = nil
70
+ redraw
71
+ end
72
+ end
73
+ table_item_edit_cancel_handler = lambda do |event|
74
+ Async::Task.new do
75
+ table_item_edit_handler.call(event, true)
76
+ end
77
+ end
78
+ table_item_edit_key_handler = lambda do |event|
79
+ Async::Task.new do
80
+ if event.key_code == 13
81
+ table_item_edit_handler.call(event)
82
+ elsif event.key_code == 27
83
+ table_item_edit_cancel_handler.call(event)
84
+ end
85
+ end
86
+ end
87
+
88
+ if @edit_column_index
89
+ table_item_input = dom_element.find("td:nth-child(#{@edit_column_index + 1}) input")
90
+ if !table_item_input.empty?
91
+ Async::Task.new do
92
+ table_item_input.focus
93
+ table_item_input.on('keyup', &table_item_edit_key_handler)
94
+ table_item_input.on('focusout', &table_item_edit_cancel_handler)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
52
100
  def edit(column_index)
53
101
  return if @edit_column_index == column_index.to_i
54
102
  parent.select(parent.index_of(self), false)
@@ -63,13 +111,14 @@ module Glimmer
63
111
  end
64
112
 
65
113
  def max_column_width(column_index)
66
- parent.dom.css("tr td:nth-child(#{column_index + 1})").first.width
114
+ parent_dom_element.find("tr td:nth-child(#{column_index + 1})").first.width
67
115
  end
68
116
 
69
117
  def dom
70
118
  table_item_id = id
71
119
  table_item_id_style = css
72
120
  table_item_css_classes = css_classes
121
+ table_item_css_classes << name
73
122
  table_item_selection = parent.selection.include?(self)
74
123
  if table_item_selection
75
124
  table_item_css_classes << 'selected'
@@ -77,40 +126,13 @@ module Glimmer
77
126
  table_item_css_classes.delete('selected')
78
127
  end
79
128
  table_item_text_array = text_array
80
- table_item_edit_column_index = @edit_column_index
81
- table_item_max_width = max_column_width(table_item_edit_column_index) if table_item_edit_column_index
82
- table_item_edit_handler = lambda do |event, cancel = false|
83
- Async::Task.new do
84
- text_value = event.target.value
85
- edit_property = parent.column_properties[table_item_edit_column_index]
86
- edit_model = get_data
87
- if !cancel && edit_model.send(edit_property) != text_value
88
- edit_model.send("#{edit_property}=", text_value)
89
- set_text(table_item_edit_column_index, text_value)
90
- end
91
- @edit_column_index = nil
92
- redraw
93
- end
94
- end
95
- table_item_edit_cancel_handler = lambda do |event|
96
- Async::Task.new do
97
- table_item_edit_handler.call(event, true)
98
- end
99
- end
100
- table_item_edit_key_handler = lambda do |event|
101
- Async::Task.new do
102
- if event.code == 13
103
- table_item_edit_handler.call(event)
104
- elsif event.code == 27
105
- table_item_edit_cancel_handler.call(event)
106
- end
107
- end
108
- end
109
- @dom ||= DOM {
129
+ table_item_max_width = max_column_width(@edit_column_index) if @edit_column_index
130
+
131
+ @dom ||= html {
110
132
  tr(id: table_item_id, style: table_item_id_style, class: table_item_css_classes.to_a.join(' ')) {
111
133
  table_item_text_array.each_with_index do |table_item_text, column_index|
112
134
  td('data-column-index' => column_index) {
113
- if table_item_edit_column_index == column_index
135
+ if @edit_column_index == column_index
114
136
  input(type: 'text', value: table_item_text, style: "max-width: #{table_item_max_width - 11}px;")
115
137
  else
116
138
  table_item_text
@@ -118,18 +140,7 @@ module Glimmer
118
140
  }
119
141
  end
120
142
  }
121
- }.tap do |the_dom|
122
- if table_item_edit_column_index
123
- table_item_input = the_dom.css("td:nth-child(#{table_item_edit_column_index + 1}) input").first
124
- if table_item_input
125
- Async::Task.new do
126
- table_item_input.focus
127
- table_item_input.on('keyup', &table_item_edit_key_handler)
128
- table_item_input.on('focusout', &table_item_edit_cancel_handler)
129
- end
130
- end
131
- end
132
- end
143
+ }.to_s
133
144
  end
134
145
  end
135
146
  end
@@ -1,9 +1,9 @@
1
- require 'glimmer/opal/element_proxy'
2
- require 'glimmer/opal/table_column'
1
+ require 'glimmer/swt/widget_proxy'
2
+ require 'glimmer/swt/table_column_proxy'
3
3
 
4
4
  module Glimmer
5
- module Opal
6
- class TableProxy < ElementProxy
5
+ module SWT
6
+ class TableProxy < WidgetProxy
7
7
  attr_reader :columns, :selection
8
8
  attr_accessor :column_properties
9
9
  alias items children
@@ -17,18 +17,17 @@ module Glimmer
17
17
 
18
18
  # Only table_columns may be added as children
19
19
  def add_child(child)
20
- if child.is_a?(TableColumn)
20
+ if child.is_a?(TableColumnProxy)
21
21
  @columns << child
22
- columns_dom << child.dom
23
22
  else
24
23
  @children << child
25
- items_dom << child.dom
26
24
  end
25
+ child.redraw
27
26
  end
28
27
 
29
28
  def remove_all
30
29
  items.clear
31
- @items_dom = nil
30
+ redraw
32
31
  end
33
32
 
34
33
  def selection=(new_selection)
@@ -36,8 +35,8 @@ module Glimmer
36
35
  @selection = new_selection
37
36
  changed.each(&:redraw)
38
37
  end
39
-
40
- def items=(new_items)
38
+
39
+ def items=(new_items)
41
40
  @children = new_items
42
41
  redraw
43
42
  end
@@ -71,66 +70,74 @@ module Glimmer
71
70
  end
72
71
 
73
72
  def observation_request_to_event_mapping
73
+ mouse_handler = -> (event_listener) {
74
+ -> (event) {
75
+ event.singleton_class.send(:define_method, :table_item=) do |item|
76
+ @table_item = item
77
+ end
78
+ event.singleton_class.send(:define_method, :table_item) do
79
+ @table_item
80
+ end
81
+ table_row = event.target.parents('tr').first
82
+ table_data = event.target.parents('td').first
83
+ event.table_item = items.detect {|item| item.id == table_row.attr('id')}
84
+ event.singleton_class.send(:define_method, :column_index) do
85
+ (table_data || event.target).attr('data-column-index')
86
+ end
87
+ event_listener.call(event)
88
+ }
89
+ }
90
+
74
91
  {
75
92
  'on_mouse_down' => {
76
93
  event: 'mousedown',
77
- event_handler: -> (event_listener) {
78
- -> (event) {
79
- event.singleton_class.send(:define_method, :table_item=) do |item|
80
- @table_item = item
81
- end
82
- event.singleton_class.send(:define_method, :table_item) do
83
- @table_item
84
- end
85
- table_row = event.target.ancestors('tr').first
86
- table_data = event.target.ancestors('td').first
87
- event.table_item = items.detect {|item| item.id == table_row.attributes['id']}
88
- event.singleton_class.send(:define_method, :column_index) do
89
- (table_data || event.target).attributes['data-column-index']
90
- end
91
- event_listener.call(event)
92
- }
93
- }
94
+ event_handler: mouse_handler,
95
+ },
96
+ 'on_mouse_up' => {
97
+ event: 'mouseup',
98
+ event_handler: mouse_handler,
94
99
  }
95
100
  }
96
101
  end
97
102
 
98
103
  def redraw
99
- if @dom
100
- old_dom = @dom
101
- @dom = nil
102
- old_dom.replace dom
103
- else
104
- dom
105
- end
106
- if @last_redrawn_children != @children
107
- items_dom.clear
108
- @last_redrawn_children = @children
109
- @children = []
110
- @last_redrawn_children.each do |child|
111
- add_child(child)
112
- end
113
- end
104
+ super()
105
+ @columns.to_a.each(&:redraw)
106
+ end
107
+
108
+ def element
109
+ 'table'
110
+ end
111
+
112
+ def columns_path
113
+ path + ' thead tr'
114
114
  end
115
115
 
116
+ def columns_dom_element
117
+ Document.find(columns_path)
118
+ end
119
+
120
+ def items_path
121
+ path + ' tbody'
122
+ end
123
+
124
+ def items_dom_element
125
+ Document.find(items_path)
126
+ end
127
+
116
128
  def columns_dom
117
- @columns_dom ||= DOM {
118
- tr {
119
- }
129
+ tr {
120
130
  }
121
131
  end
122
132
 
123
133
  def thead_dom
124
- @thead_dom ||= DOM {
125
- thead {
126
- }
127
- }.tap {|the_dom| the_dom << columns_dom }
128
- end
134
+ thead {
135
+ columns_dom
136
+ }
137
+ end
129
138
 
130
139
  def items_dom
131
- @items_dom ||= DOM {
132
- tbody {
133
- }
140
+ tbody {
134
141
  }
135
142
  end
136
143
 
@@ -138,11 +145,14 @@ module Glimmer
138
145
  table_id = id
139
146
  table_id_style = css
140
147
  table_id_css_classes = css_classes
148
+ table_id_css_classes << 'table'
141
149
  table_id_css_classes_string = table_id_css_classes.to_a.join(' ')
142
- @dom ||= DOM {
143
- table(id: table_id, style: table_id_style, class: table_id_css_classes_string) {
150
+ @dom ||= html {
151
+ table(id: table_id, style: table_id_style, class: table_id_css_classes_string) {
152
+ thead_dom
153
+ items_dom
144
154
  }
145
- }.tap {|the_dom| the_dom >> thead_dom }.tap {|the_dom| the_dom << items_dom }
155
+ }.to_s
146
156
  end
147
157
  end
148
158
  end
@@ -0,0 +1,46 @@
1
+ require 'glimmer/swt/widget_proxy'
2
+
3
+ module Glimmer
4
+ module SWT
5
+ class TextProxy < WidgetProxy
6
+ attr_reader :text
7
+
8
+ def text=(value)
9
+ @text = value
10
+ Document.find(path).value = value
11
+ end
12
+
13
+ def element
14
+ 'input'
15
+ end
16
+
17
+ def observation_request_to_event_mapping
18
+ {
19
+ 'on_modify_text' => {
20
+ event: 'keyup',
21
+ event_handler: -> (event_listener) {
22
+ -> (event) {
23
+ @text = event.target.value
24
+ event_listener.call(event)
25
+ }
26
+ }
27
+ }
28
+ }
29
+ end
30
+
31
+ def dom
32
+ text_text = @text
33
+ text_id = id
34
+ text_style = css
35
+ text_class = name
36
+ # TODO support password field
37
+ options = {type: 'text', id: text_id, style: text_style, class: text_class, value: text_text, style: 'min-width: 27px;'}
38
+ options = options.merge('disabled': 'disabled') unless @enabled
39
+ options = options.merge(type: 'password') if has_style?(:password)
40
+ @dom ||= html {
41
+ input(options)
42
+ }.to_s
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,511 @@
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/event_listener_proxy'
23
+ require 'glimmer/swt/property_owner'
24
+ require 'glimmer/swt/swt_proxy'
25
+
26
+ module Glimmer
27
+ module SWT
28
+ class WidgetProxy
29
+ include Glimmer
30
+ include PropertyOwner
31
+
32
+ attr_reader :parent, :args, :path, :children, :enabled, :foreground, :background, :font, :focus
33
+
34
+ class << self
35
+ # Factory Method that translates a Glimmer DSL keyword into a WidgetProxy object
36
+ def for(keyword, parent, args)
37
+ the_widget_class = widget_class(keyword)
38
+ the_widget_class.respond_to?(:create) ? the_widget_class.create(parent, args) : the_widget_class.new(parent, args)
39
+ end
40
+
41
+ def widget_class(keyword)
42
+ class_name_alternative = keyword.camelcase(:upper)
43
+ class_name_main = "#{class_name_alternative}Proxy"
44
+ Glimmer::SWT.const_get(class_name_main.to_sym) rescue Glimmer::SWT.const_get(class_name_alternative.to_sym)
45
+ rescue => e
46
+ puts "Widget #{keyword} was not found!"
47
+ nil
48
+ end
49
+
50
+ def widget_exists?(keyword)
51
+ !!widget_class(keyword)
52
+ end
53
+
54
+ def next_id_number_for(name)
55
+ @max_id_numbers[name] = max_id_number_for(name) + 1
56
+ end
57
+
58
+ def max_id_number_for(name)
59
+ @max_id_numbers[name] = max_id_numbers[name] || 0
60
+ end
61
+
62
+ def max_id_numbers
63
+ @max_id_numbers ||= reset_max_id_numbers!
64
+ end
65
+
66
+ def reset_max_id_numbers!
67
+ @max_id_numbers = {}
68
+ end
69
+
70
+ def underscored_widget_name(widget_proxy)
71
+ widget_proxy.class.name.split(/::|\./).last.sub(/Proxy$/, '').underscore
72
+ end
73
+ end
74
+
75
+ DEFAULT_INITIALIZERS = {
76
+ "composite" => lambda do |composite_proxy|
77
+ if composite_proxy.layout.nil?
78
+ layout = GridLayoutProxy.new(composite_proxy, [])
79
+ composite_proxy.layout = layout
80
+ layout.margin_width = 15
81
+ layout.margin_height = 15
82
+ end
83
+ end,
84
+ # "scrolled_composite" => lambda do |scrolled_composite|
85
+ # scrolled_composite.expand_horizontal = true
86
+ # scrolled_composite.expand_vertical = true
87
+ # end,
88
+ # "table" => lambda do |table|
89
+ # table.setHeaderVisible(true)
90
+ # table.setLinesVisible(true)
91
+ # end,
92
+ "table_column" => lambda do |table_column_proxy|
93
+ table_column_proxy.width = 80
94
+ end,
95
+ # "group" => lambda do |group_proxy|
96
+ # group_proxy.layout = GridLayoutProxy.new(group_proxy, []) if group.layout.nil?
97
+ # end,
98
+ }
99
+
100
+ def initialize(parent, args)
101
+ @parent = parent
102
+ @args = args
103
+ @children = Set.new # TODO consider moving to composite
104
+ @enabled = true
105
+ DEFAULT_INITIALIZERS[self.class.underscored_widget_name(self)]&.call(self)
106
+ @parent.add_child(self) # TODO rename to post_initialize_child to be closer to glimmer-dsl-swt terminology
107
+ end
108
+
109
+ def css_classes
110
+ dom_element.attr('class').to_s.split
111
+ end
112
+
113
+ def dispose
114
+ Document.find(path).remove
115
+ end
116
+
117
+ def path
118
+ "#{parent_path} #{element}##{id}.#{name}"
119
+ end
120
+
121
+ # Root element representing widget. Must be overridden by subclasses if different from div
122
+ def element
123
+ 'div'
124
+ end
125
+
126
+ def add_child(child)
127
+ @children << child
128
+ child.render
129
+ end
130
+
131
+ def enabled=(value)
132
+ @enabled = value
133
+ dom_element.prop('disabled', !@enabled)
134
+ end
135
+
136
+ def foreground=(value)
137
+ @foreground = value
138
+ dom_element.css('color', foreground.to_css) unless foreground.nil?
139
+ end
140
+
141
+ def background=(value)
142
+ @background = value
143
+ dom_element.css('background-color', background.to_css) unless background.nil?
144
+ end
145
+
146
+ def font=(value)
147
+ @font = value.is_a?(FontProxy) ? value : FontProxy.new(self, value)
148
+ dom_element.css('font-family', @font.name) unless @font.nil?
149
+ dom_element.css('font-style', 'italic') if @font&.style == :italic
150
+ dom_element.css('font-weight', 'bold') if @font&.style == :bold
151
+ dom_element.css('font-size', "#{@font.height}px") unless @font.nil?
152
+ end
153
+
154
+ def focus=(value)
155
+ @focus = value
156
+ dom_element.focus # TODO consider if a delay or async_exec is needed here
157
+ end
158
+
159
+ def set_focus
160
+ self.focus = true
161
+ end
162
+ alias setFocus set_focus
163
+
164
+ def parent_path
165
+ @parent.path
166
+ end
167
+
168
+ def render
169
+ old_element = dom_element
170
+ brand_new = @dom.nil? || old_element.empty?
171
+ build_dom
172
+ if brand_new
173
+ Document.find(parent_path).append(@dom)
174
+ else
175
+ old_element.replace_with(@dom)
176
+ end
177
+ @observation_requests&.clone&.each do |keyword, event_listener_set|
178
+ event_listener_set.each do |event_listener|
179
+ @observation_requests[keyword].delete(event_listener)
180
+ handle_observation_request(keyword, &event_listener)
181
+ end
182
+ end
183
+ children.each do |child|
184
+ child.render
185
+ end
186
+ end
187
+ alias redraw render
188
+
189
+ def build_dom
190
+ @dom = nil
191
+ @dom = dom
192
+ @dom = @parent.layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.layout
193
+ end
194
+
195
+ def content(&block)
196
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::WidgetExpression.new, &block)
197
+ end
198
+
199
+ # Subclasses must override with their own mappings
200
+ def observation_request_to_event_mapping
201
+ {}
202
+ end
203
+
204
+ def name
205
+ self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-')
206
+ end
207
+
208
+ def id
209
+ @id ||= "#{name}-#{WidgetProxy.next_id_number_for(name)}"
210
+ end
211
+
212
+ # Sets id explicitly. Useful in cases of wanting to maintain a stable id
213
+ def id=(value)
214
+ @id = value
215
+ end
216
+
217
+ # Subclasses can override with their own selector
218
+ def selector
219
+ "#{name}##{id}"
220
+ end
221
+
222
+ def add_css_class(css_class)
223
+ dom_element.add_class(css_class)
224
+ end
225
+
226
+ def add_css_classes(css_classes_to_add)
227
+ css_classes_to_add.each {|css_class| add_css_class(css_class)}
228
+ end
229
+
230
+ def remove_css_class(css_class)
231
+ dom_element.remove_class(css_class)
232
+ end
233
+
234
+ def remove_css_classes(css_classes_to_remove)
235
+ css_classes_to_remove.each {|css_class| remove_css_class(css_class)}
236
+ end
237
+
238
+ def clear_css_classes
239
+ css_classes.each {|css_class| remove_css_class(css_class)}
240
+ end
241
+
242
+ def has_style?(symbol)
243
+ @args.include?(symbol) # not a very solid implementation. Bring SWT constants eventually
244
+ end
245
+
246
+ def dom_element
247
+ # TODO consider making this pick an element in relation to its parent, allowing unhooked dom elements to be built if needed (unhooked to the visible page dom)
248
+ Document.find(path)
249
+ end
250
+
251
+ def style_element
252
+ style_element_id = "#{id}-style"
253
+ style_element_selector = "style##{style_element_id}"
254
+ element = dom_element.find(style_element_selector)
255
+ if element.empty?
256
+ new_element = Element.new(:style)
257
+ new_element.attr('id', style_element_id)
258
+ new_element.attr('class', "#{name.gsub('_', '-')}-instance-style widget-instance-style")
259
+ dom_element.prepend(new_element)
260
+ element = dom_element.find(style_element_selector)
261
+ end
262
+ element
263
+ end
264
+
265
+ def parent_dom_element
266
+ Document.find(parent_path)
267
+ end
268
+
269
+ def listener_path
270
+ path
271
+ end
272
+
273
+ def listener_dom_element
274
+ Document.find(listener_path)
275
+ end
276
+
277
+ def can_handle_observation_request?(observation_request)
278
+ # TODO sort this out for Opal
279
+ observation_request = observation_request.to_s
280
+ if observation_request.start_with?('on_swt_')
281
+ constant_name = observation_request.sub(/^on_swt_/, '')
282
+ SWTProxy.has_constant?(constant_name)
283
+ elsif observation_request.start_with?('on_')
284
+ # event = observation_request.sub(/^on_/, '')
285
+ # can_add_listener?(event) || can_handle_drag_observation_request?(observation_request) || can_handle_drop_observation_request?(observation_request)
286
+ true # TODO filter by valid listeners only in the future
287
+ end
288
+ end
289
+
290
+ def handle_observation_request(keyword, &event_listener)
291
+ return unless observation_request_to_event_mapping.keys.include?(keyword)
292
+ @observation_requests ||= {}
293
+ @observation_requests[keyword] ||= Set.new
294
+ event = nil
295
+ delegate = nil
296
+ [observation_request_to_event_mapping[keyword]].flatten.each do |mapping|
297
+ @observation_requests[keyword] << event_listener
298
+ event = mapping[:event]
299
+ event_handler = mapping[:event_handler]
300
+ potential_event_listener = event_handler&.call(event_listener)
301
+ event_listener = potential_event_listener || event_listener
302
+ delegate = listener_dom_element.on(event, &event_listener)
303
+ end
304
+ # TODO update code below for new WidgetProxy API
305
+ EventListenerProxy.new(element_proxy: self, event: event, selector: selector, delegate: delegate)
306
+ end
307
+
308
+ def add_observer(observer, property_name)
309
+ property_listener_installers = self.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
310
+ widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
311
+ widget_listener_installers.to_a.each do |widget_listener_installer|
312
+ widget_listener_installer.call(observer)
313
+ end
314
+ end
315
+
316
+ def set_attribute(attribute_name, *args)
317
+ apply_property_type_converters(attribute_name, args)
318
+ super(attribute_name, *args) # PropertyOwner
319
+ end
320
+
321
+ def apply_property_type_converters(attribute_name, args)
322
+ if args.count == 1
323
+ value = args.first
324
+ converter = property_type_converters[attribute_name.to_sym]
325
+ args[0] = converter.call(value) if converter
326
+ end
327
+ # if args.count == 1 && args.first.is_a?(ColorProxy)
328
+ # g_color = args.first
329
+ # args[0] = g_color.swt_color
330
+ # end
331
+ end
332
+
333
+ def property_type_converters
334
+ color_converter = lambda do |value|
335
+ if value.is_a?(Symbol) || value.is_a?(String)
336
+ ColorProxy.new(value)
337
+ else
338
+ value
339
+ end
340
+ end
341
+ @property_type_converters ||= {
342
+ :background => color_converter,
343
+ # :background_image => lambda do |value|
344
+ # if value.is_a?(String)
345
+ # if value.start_with?('uri:classloader')
346
+ # value = value.sub(/^uri\:classloader\:\//, '')
347
+ # object = java.lang.Object.new
348
+ # value = object.java_class.resource_as_stream(value)
349
+ # value = java.io.BufferedInputStream.new(value)
350
+ # end
351
+ # image_data = ImageData.new(value)
352
+ # on_event_Resize do |resize_event|
353
+ # new_image_data = image_data.scaledTo(@swt_widget.getSize.x, @swt_widget.getSize.y)
354
+ # @swt_widget.getBackgroundImage&.dispose
355
+ # @swt_widget.setBackgroundImage(Image.new(@swt_widget.getDisplay, new_image_data))
356
+ # end
357
+ # Image.new(@swt_widget.getDisplay, image_data)
358
+ # else
359
+ # value
360
+ # end
361
+ # end,
362
+ :foreground => color_converter,
363
+ # :font => lambda do |value|
364
+ # if value.is_a?(Hash)
365
+ # font_properties = value
366
+ # FontProxy.new(self, font_properties).swt_font
367
+ # else
368
+ # value
369
+ # end
370
+ # end,
371
+ :items => lambda do |value|
372
+ value.to_java :string
373
+ end,
374
+ :text => lambda do |value|
375
+ # if swt_widget.is_a?(Browser)
376
+ # value.to_s
377
+ # else
378
+ value.to_s
379
+ # end
380
+ end,
381
+ # :visible => lambda do |value|
382
+ # !!value
383
+ # end,
384
+ }
385
+ end
386
+
387
+ def widget_property_listener_installers
388
+ @swt_widget_property_listener_installers ||= {
389
+ # WidgetProxy => {
390
+ # :focus => lambda do |observer|
391
+ # on_focus_gained { |focus_event|
392
+ # observer.call(true)
393
+ # }
394
+ # on_focus_lost { |focus_event|
395
+ # observer.call(false)
396
+ # }
397
+ # end,
398
+ # },
399
+ TextProxy => {
400
+ :text => lambda do |observer|
401
+ on_modify_text { |modify_event|
402
+ observer.call(text)
403
+ }
404
+ end,
405
+ # :caret_position => lambda do |observer|
406
+ # on_event_keydown { |event|
407
+ # observer.call(getCaretPosition)
408
+ # }
409
+ # on_event_keyup { |event|
410
+ # observer.call(getCaretPosition)
411
+ # }
412
+ # on_event_mousedown { |event|
413
+ # observer.call(getCaretPosition)
414
+ # }
415
+ # on_event_mouseup { |event|
416
+ # observer.call(getCaretPosition)
417
+ # }
418
+ # end,
419
+ # :selection => lambda do |observer|
420
+ # on_event_keydown { |event|
421
+ # observer.call(getSelection)
422
+ # }
423
+ # on_event_keyup { |event|
424
+ # observer.call(getSelection)
425
+ # }
426
+ # on_event_mousedown { |event|
427
+ # observer.call(getSelection)
428
+ # }
429
+ # on_event_mouseup { |event|
430
+ # observer.call(getSelection)
431
+ # }
432
+ # end,
433
+ # :selection_count => lambda do |observer|
434
+ # on_event_keydown { |event|
435
+ # observer.call(getSelectionCount)
436
+ # }
437
+ # on_event_keyup { |event|
438
+ # observer.call(getSelectionCount)
439
+ # }
440
+ # on_event_mousedown { |event|
441
+ # observer.call(getSelectionCount)
442
+ # }
443
+ # on_event_mouseup { |event|
444
+ # observer.call(getSelectionCount)
445
+ # }
446
+ # end,
447
+ # :top_index => lambda do |observer|
448
+ # @last_top_index = getTopIndex
449
+ # on_paint_control { |event|
450
+ # if getTopIndex != @last_top_index
451
+ # @last_top_index = getTopIndex
452
+ # observer.call(@last_top_index)
453
+ # end
454
+ # }
455
+ # end,
456
+ },
457
+ # Java::OrgEclipseSwtCustom::StyledText => {
458
+ # :text => lambda do |observer|
459
+ # on_modify_text { |modify_event|
460
+ # observer.call(getText)
461
+ # }
462
+ # end,
463
+ # },
464
+ RadioProxy => { #radio?
465
+ :selection => lambda do |observer|
466
+ on_widget_selected { |selection_event|
467
+ observer.call(selection)
468
+ }
469
+ end
470
+ },
471
+ # Java::OrgEclipseSwtWidgets::MenuItem => {
472
+ # :selection => lambda do |observer|
473
+ # on_widget_selected { |selection_event|
474
+ # observer.call(getSelection)
475
+ # }
476
+ # end
477
+ # },
478
+ # Java::OrgEclipseSwtWidgets::Spinner => {
479
+ # :selection => lambda do |observer|
480
+ # on_widget_selected { |selection_event|
481
+ # observer.call(getSelection)
482
+ # }
483
+ # end
484
+ # },
485
+ }
486
+ end
487
+
488
+ end
489
+ end
490
+ end
491
+
492
+ require 'glimmer/swt/browser_proxy'
493
+ require 'glimmer/swt/button_proxy'
494
+ require 'glimmer/swt/combo_proxy'
495
+ require 'glimmer/swt/checkbox_proxy'
496
+ require 'glimmer/swt/composite_proxy'
497
+ require 'glimmer/swt/group_proxy'
498
+ require 'glimmer/swt/label_proxy'
499
+ require 'glimmer/swt/list_proxy'
500
+ require 'glimmer/swt/radio_proxy'
501
+ require 'glimmer/swt/tab_folder_proxy'
502
+ require 'glimmer/swt/tab_item_proxy'
503
+ require 'glimmer/swt/table_column_proxy'
504
+ require 'glimmer/swt/table_item_proxy'
505
+ require 'glimmer/swt/table_proxy'
506
+ require 'glimmer/swt/text_proxy'
507
+ require 'glimmer/swt/radio_proxy'
508
+ require 'glimmer/swt/scrolled_composite_proxy'
509
+ require 'glimmer/swt/styled_text_proxy'
510
+
511
+ require 'glimmer/dsl/opal/widget_expression'