glimmer-dsl-opal 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +453 -48
  4. data/VERSION +1 -1
  5. data/lib/display.rb +31 -0
  6. data/lib/glimmer-dsl-opal.rb +15 -36
  7. data/lib/glimmer-dsl-opal/ext/class.rb +10 -0
  8. data/lib/glimmer-dsl-opal/ext/file.rb +29 -0
  9. data/lib/glimmer-dsl-opal/ext/struct.rb +37 -0
  10. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager.rb +50 -23
  11. data/lib/glimmer-dsl-opal/samples/elaborate/login.rb +22 -5
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_browser.rb +24 -1
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +46 -0
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +27 -0
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +7 -7
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +62 -32
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +47 -22
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_message_box.rb +37 -0
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_pop_up_context_menu.rb +84 -0
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +2 -2
  21. data/lib/glimmer/data_binding/observable_element.rb +1 -1
  22. data/lib/glimmer/data_binding/table_items_binding.rb +3 -3
  23. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +6 -0
  24. data/lib/glimmer/dsl/opal/dsl.rb +2 -0
  25. data/lib/glimmer/dsl/opal/menu_bar_expression.rb +54 -0
  26. data/lib/glimmer/dsl/opal/menu_expression.rb +61 -0
  27. data/lib/glimmer/dsl/opal/widget_expression.rb +3 -2
  28. data/lib/glimmer/dsl/opal/widget_listener_expression.rb +2 -2
  29. data/lib/glimmer/swt/combo_proxy.rb +40 -1
  30. data/lib/glimmer/swt/control_editor.rb +2 -1
  31. data/lib/glimmer/swt/custom/checkbox_group.rb +2 -2
  32. data/lib/glimmer/swt/custom/radio_group.rb +2 -2
  33. data/lib/glimmer/swt/date_time_proxy.rb +66 -1
  34. data/lib/glimmer/swt/event_listener_proxy.rb +14 -4
  35. data/lib/glimmer/swt/font_proxy.rb +4 -4
  36. data/lib/glimmer/swt/grid_layout_proxy.rb +21 -12
  37. data/lib/glimmer/swt/label_proxy.rb +17 -6
  38. data/lib/glimmer/swt/layout_data_proxy.rb +10 -7
  39. data/lib/glimmer/swt/list_proxy.rb +33 -0
  40. data/lib/glimmer/swt/menu_item_proxy.rb +87 -0
  41. data/lib/glimmer/swt/menu_proxy.rb +162 -0
  42. data/lib/glimmer/swt/message_box_proxy.rb +53 -67
  43. data/lib/glimmer/swt/property_owner.rb +2 -0
  44. data/lib/glimmer/swt/radio_proxy.rb +1 -1
  45. data/lib/glimmer/swt/shell_proxy.rb +32 -187
  46. data/lib/glimmer/swt/tab_folder_proxy.rb +43 -0
  47. data/lib/glimmer/swt/table_column_proxy.rb +4 -3
  48. data/lib/glimmer/swt/table_editor.rb +2 -2
  49. data/lib/glimmer/swt/table_item_proxy.rb +15 -5
  50. data/lib/glimmer/swt/table_proxy.rb +34 -12
  51. data/lib/glimmer/swt/text_proxy.rb +1 -1
  52. data/lib/glimmer/swt/widget_proxy.rb +335 -38
  53. data/lib/glimmer/ui/custom_shell.rb +9 -7
  54. data/lib/glimmer/ui/custom_widget.rb +3 -3
  55. data/lib/os.rb +36 -0
  56. metadata +36 -3
@@ -4,16 +4,19 @@ require 'glimmer/swt/widget_proxy'
4
4
  module Glimmer
5
5
  module SWT
6
6
  class LabelProxy < WidgetProxy
7
- attr_reader :text, :background_image, :image, :alignment
7
+ attr_reader :text, :background_image, :image
8
8
 
9
9
  def initialize(parent, args, block)
10
10
  super(parent, args, block)
11
- self.alignment = [:left, :center, :right].detect {|align| args.detect { |arg| SWTProxy[align] == arg } }
12
11
  end
13
12
 
14
13
  def text=(value)
15
14
  @text = value
16
- dom_element.html(value)
15
+ dom_element.html(html_text)
16
+ end
17
+
18
+ def html_text
19
+ text.gsub("\n", '<br />')
17
20
  end
18
21
 
19
22
  def background_image=(*image_options)
@@ -32,6 +35,15 @@ module Glimmer
32
35
  def element
33
36
  'label'
34
37
  end
38
+
39
+ def alignment
40
+ if @alignment.nil?
41
+ found_arg = nil
42
+ @alignment = [:left, :center, :right].detect {|align| found_arg = args.detect { |arg| SWTProxy[align] == SWTProxy[arg] } }
43
+ args.delete(found_arg)
44
+ end
45
+ @alignment
46
+ end
35
47
 
36
48
  def alignment=(value)
37
49
  # TODO consider storing swt value in the future instead
@@ -40,12 +52,11 @@ module Glimmer
40
52
  end
41
53
 
42
54
  def dom
43
- label_text = @text
44
55
  label_id = id
45
56
  label_class = name
46
57
  @dom ||= html {
47
- label(id: label_id, class: label_class) {
48
- label_text
58
+ label(id: label_id, class: label_class, style: "text-align: #{alignment};") {
59
+ html_text
49
60
  }
50
61
  }.to_s
51
62
  end
@@ -3,6 +3,7 @@ require 'glimmer/swt/property_owner'
3
3
  module Glimmer
4
4
  module SWT
5
5
  class LayoutDataProxy
6
+ # TODO make this polymorphic as GridData or RowData subclasses
6
7
  include Glimmer::SWT::PropertyOwner
7
8
  attr_reader :parent,
8
9
  :args,
@@ -44,10 +45,11 @@ module Glimmer
44
45
  def horizontal_alignment=(horizontal_alignment)
45
46
  @horizontal_alignment = horizontal_alignment
46
47
  return if @horizontal_alignment.nil?
47
- if @horizontal_alignment == 'fill'
48
- @parent.dom_element.css('width', '100%') if width_hint.nil?
49
- else
50
- @parent.dom_element.css('text-align', @horizontal_alignment)
48
+ if @horizontal_alignment != 'fill'
49
+ @parent.dom_element.css('text-align', @horizontal_alignment.to_s)
50
+ @parent.dom_element.css('place-self', @horizontal_alignment.to_s)
51
+ @parent.dom_element.css('margin-left', 'auto') if ['right', 'center'].include?(@horizontal_alignment.to_s)
52
+ @parent.dom_element.css('margin-right', 'auto') if ['left', 'center'].include?(@horizontal_alignment.to_s)
51
53
  end
52
54
  # TODO
53
55
  # reapply
@@ -85,18 +87,19 @@ module Glimmer
85
87
 
86
88
  def grab_excess_horizontal_space=(grab_excess_horizontal_space)
87
89
  @grab_excess_horizontal_space = grab_excess_horizontal_space
88
- @parent.dom_element.css('width', "100%") if @grab_excess_horizontal_space && width_hint.nil?
90
+ @parent.dom_element.css('justify-self', @horizontal_alignment) if @grab_excess_horizontal_space && @horizontal_alignment != 'fill' && width_hint.nil?
91
+ @parent.parent.dom_element.css('justify-content', "normal") if @grab_excess_horizontal_space
89
92
  # reapply
90
93
  end
91
94
 
92
95
  def grab_excess_vertical_space=(grab_excess_vertical_space)
93
96
  @grab_excess_vertical_space = grab_excess_vertical_space
94
- @parent.dom_element.css('height', "100%") if @grab_excess_vertical_space && height_hint.nil?
95
- # TODO
97
+ @parent.dom_element.css('height', "100%") if @grab_excess_vertical_space && @vertical_alignment == 'fill' && height_hint.nil?
96
98
  # reapply
97
99
  end
98
100
 
99
101
  def reapply
102
+ # TODO remove reapply method
100
103
  # @parent.css = <<~CSS
101
104
  # CSS
102
105
  end
@@ -3,6 +3,39 @@ require 'glimmer/swt/widget_proxy'
3
3
  module Glimmer
4
4
  module SWT
5
5
  class ListProxy < WidgetProxy
6
+ STYLE = <<~CSS
7
+ ul {
8
+ list-style: none;
9
+ padding: 0;
10
+ }
11
+ li {
12
+ cursor: default;
13
+ padding-left: 10px;
14
+ padding-right: 10px;
15
+ }
16
+ li.menu-item {
17
+ padding-left: initial;
18
+ padding-right: initial;
19
+ }
20
+ .ui-menu {
21
+ /* TODO consider auto-sizing in the future */
22
+ font-size: initial;
23
+ width: 150px;
24
+ border-radius: 5px;
25
+ }
26
+ .ui-menu-item:first-child > .ui-menu-item-wrapper {
27
+ border-top-left-radius: 5px;
28
+ border-top-right-radius: 5px;
29
+ }
30
+ .ui-menu-item:last-child > .ui-menu-item-wrapper {
31
+ border-bottom-left-radius: 5px;
32
+ border-bottom-right-radius: 5px;
33
+ }
34
+ li.empty-list-item {
35
+ color: transparent;
36
+ }
37
+ CSS
38
+
6
39
  ITEM_EMPTY = '_____'
7
40
  attr_reader :items, :selection
8
41
 
@@ -0,0 +1,87 @@
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
+ # TODO implement set_menu or self.menu=
23
+
24
+ require 'glimmer/swt/widget_proxy'
25
+
26
+ module Glimmer
27
+ module SWT
28
+ # Proxy for org.eclipse.swt.widgets.MenuItem
29
+ #
30
+ # Follows the Proxy Design Pattern since it's a proxy for an HTML based menu
31
+ # Follows the Adapter Design Pattern since it's adapting a Glimmer DSL for SWT widget
32
+ class MenuItemProxy < WidgetProxy
33
+ def post_initialize_child(child)
34
+ @children << child
35
+ end
36
+
37
+ def text
38
+ @text
39
+ end
40
+
41
+ def text=(value)
42
+ @text = value
43
+ dom_element.html(html {div {value}}.to_s)
44
+ @text
45
+ end
46
+
47
+ def root_menu
48
+ the_menu = parent
49
+ the_menu = the_menu.parent_menu until the_menu.root_menu?
50
+ the_menu
51
+ end
52
+
53
+ def skip_content_on_render_blocks?
54
+ true
55
+ end
56
+
57
+ def observation_request_to_event_mapping
58
+ {
59
+ 'on_widget_selected' => {
60
+ event: 'mouseup',
61
+ event_handler: -> (event_listener) {
62
+ -> (event) {
63
+ remove_event_listener_proxies
64
+ root_menu.close
65
+ event_listener.call(event)
66
+ }
67
+ },
68
+ },
69
+ }
70
+ end
71
+
72
+ def element
73
+ 'li'
74
+ end
75
+
76
+ def dom
77
+ @dom ||= html {
78
+ li(id: id, class: name) {
79
+ div {
80
+ @text
81
+ }
82
+ }
83
+ }.to_s
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,162 @@
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/widget_proxy'
23
+ require 'glimmer/swt/menu_item_proxy'
24
+
25
+ module Glimmer
26
+ module SWT
27
+ # Proxy for org.eclipse.swt.widgets.Menu
28
+ #
29
+ # Functions differently from other widget proxies.
30
+ #
31
+ # Glimmer automatically detects if this is a drop down menu
32
+ # or pop up menu from its parent if no SWT style is passed in.
33
+ #
34
+ # There are 3 possibilities:
35
+ # - SWT :bar style is passed in: Menu Bar
36
+ # - Parent is ShellProxy: Pop Up Menu (having style :pop_up)
37
+ # - Parent is another Menu: Drop Down Menu (having style :drop_down)
38
+ #
39
+ # In order to get the SWT Menu object, one must call `#swt_widget`.
40
+ #
41
+ # In the case of a Drop Down menu, this automatically creates an
42
+ # SWT MenuItem object with style :cascade
43
+ #
44
+ # In order to retrieve the menu item widget proxy, one must call `#menu_item_proxy`
45
+ #
46
+ # Follows the Proxy Design Pattern
47
+ class MenuProxy < WidgetProxy
48
+ attr_reader :menu_item_proxy, :menu_parent
49
+
50
+ def initialize(parent, args)
51
+ # TODO handle :bar swt style
52
+ # TODO handle :pop_up swt style
53
+ # TODO handle :cascade swt style
54
+ @children = []
55
+ index = args.delete(args.last) if args.last.is_a?(Numeric)
56
+ styles = args.map(&:to_sym)
57
+ if !styles.include?(:bar) && !parent.is_a?(MenuProxy)
58
+ styles = styles.unshift(:pop_up)
59
+ end
60
+
61
+ if parent.is_a?(MenuProxy)
62
+ @menu_item_proxy = SWT::WidgetProxy.for('menu_item', parent, [:cascade] + [index].compact)
63
+ super(@menu_item_proxy)
64
+ @menu_item_proxy.menu = self
65
+ elsif parent.is_a?(ShellProxy)
66
+ super(parent, style('menu', styles))
67
+ else
68
+ super(parent)
69
+ end
70
+
71
+ if styles.include?(:bar)
72
+ # Assumes a parent shell
73
+ parent.menu_bar = self
74
+ elsif styles.include?(:pop_up)
75
+ parent.menu = self
76
+ end
77
+ # TODO IMPLEMENT PROPERLY
78
+ # on_focus_lost {
79
+ # dispose
80
+ # }
81
+ end
82
+
83
+ def text
84
+ @menu_item_proxy&.text
85
+ end
86
+
87
+ def text=(text_value)
88
+ @menu_item_proxy&.text = text_value
89
+ end
90
+
91
+ def can_handle_observation_request?(observation_request, super_only: false)
92
+ super_result = super(observation_request)
93
+ if observation_request.start_with?('on_') && !super_result && !super_only
94
+ return menu_item_proxy.can_handle_observation_request?(observation_request)
95
+ else
96
+ super_result
97
+ end
98
+ end
99
+
100
+ def handle_observation_request(observation_request, block)
101
+ if can_handle_observation_request?(observation_request, super_only: true)
102
+ super
103
+ else
104
+ menu_item_proxy.handle_observation_request(observation_request, block)
105
+ end
106
+ end
107
+
108
+ def post_initialize_child(child)
109
+ if child && !@children.include?(child)
110
+ if child.is_a?(MenuItemProxy)
111
+ @children << child
112
+ else
113
+ @children << child.menu_item_proxy
114
+ end
115
+ end
116
+ end
117
+
118
+ def render
119
+ # TODO attach to top nav bar if parent is shell
120
+ # TODO attach listener to parent to display on right click
121
+ if parent.is_a?(MenuProxy) || parent.is_a?(MenuItemProxy) || parent.menu_requested?
122
+ super
123
+ if root_menu?
124
+ id_css = "##{id}"
125
+ `$(#{id_css}).menu();`
126
+ @close_event_handler = lambda do |event|
127
+ if event.target.parents('.ui-menu').empty?
128
+ close
129
+ end
130
+ end
131
+ Element['body'].on('click', &@close_event_handler)
132
+ @menu_initialized = true
133
+ end
134
+ end
135
+ end
136
+
137
+ def close
138
+ dom_element.remove
139
+ Element['body'].off('click', &@close_event_handler)
140
+ end
141
+
142
+ def root_menu?
143
+ !parent.is_a?(MenuProxy) && !parent.is_a?(MenuItemProxy)
144
+ end
145
+
146
+ def parent_menu
147
+ parent.parent unless root_menu?
148
+ end
149
+
150
+ def element
151
+ 'ul'
152
+ end
153
+
154
+ def dom
155
+ @dom ||= html {
156
+ ul(id: id, class: name) {
157
+ }
158
+ }.to_s
159
+ end
160
+ end
161
+ end
162
+ end
@@ -4,11 +4,54 @@ require 'glimmer/swt/display_proxy'
4
4
  module Glimmer
5
5
  module SWT
6
6
  class MessageBoxProxy < WidgetProxy
7
+ STYLE = <<~CSS
8
+ .modal {
9
+ position: fixed;
10
+ z-index: 1;
11
+ padding-top: 100px;
12
+ left: 0;
13
+ top: 0;
14
+ width: 100%;
15
+ height: 100%;
16
+ overflow: auto;
17
+ background-color: rgb(0,0,0);
18
+ background-color: rgba(0,0,0,0.4);
19
+ text-align: center;
20
+ }
21
+ .modal-content .text {
22
+ background: rgb(80, 116, 211);
23
+ color: white;
24
+ padding: 5px;
25
+ }
26
+ .modal-content .message {
27
+ padding: 20px;
28
+ }
29
+ .modal-content {
30
+ background-color: #fefefe;
31
+ padding-bottom: 15px;
32
+ border: 1px solid #888;
33
+ display: inline-block;
34
+ min-width: 200px;
35
+ }
36
+ CSS
37
+ # .close {
38
+ # color: #aaaaaa;
39
+ # float: right;
40
+ # font-weight: bold;
41
+ # margin: 5px;
42
+ # }
43
+ # .close:hover,
44
+ # .close:focus {
45
+ # color: #000;
46
+ # text-decoration: none;
47
+ # cursor: pointer;
48
+ # }
49
+
7
50
  attr_reader :text, :message
8
51
 
9
52
  def initialize(parent, args, block)
10
53
  i = 0
11
- @parent = parent || DisplayProxy.instance.shells.first
54
+ @parent = parent || DisplayProxy.instance.shells.last
12
55
  @args = args
13
56
  @block = block
14
57
  @children = Set.new
@@ -25,21 +68,17 @@ module Glimmer
25
68
  dom_element.find('.modal-content .text').html(@text)
26
69
  end
27
70
 
28
- def message=(msg)
29
- @message = msg
30
- dom_element.find('.modal-content .message').html(@text)
71
+ def html_message
72
+ message.gsub("\n", '<br />')
31
73
  end
32
74
 
33
- def document
34
- element = self
35
- begin
36
- element = element.parent
37
- end while(element.parent)
38
- element
75
+ def message=(msg)
76
+ @message = msg
77
+ dom_element.find('.modal-content .message').html(html_message)
39
78
  end
40
79
 
41
80
  def open
42
- document.post_initialize_child(self)
81
+ parent.post_initialize_child(self)
43
82
  end
44
83
 
45
84
  def hide
@@ -70,68 +109,15 @@ module Glimmer
70
109
  }
71
110
  end
72
111
 
73
- def style_dom_modal_css
74
- <<~CSS
75
- .modal {
76
- position: fixed;
77
- z-index: 1;
78
- padding-top: 100px;
79
- left: 0;
80
- top: 0;
81
- width: 100%;
82
- height: 100%;
83
- overflow: auto;
84
- background-color: rgb(0,0,0);
85
- background-color: rgba(0,0,0,0.4);
86
- text-align: center;
87
- }
88
- .modal-content .text {
89
- background: rgb(80, 116, 211);
90
- color: white;
91
- padding: 5px;
92
- }
93
- .modal-content .message {
94
- padding: 20px;
95
- }
96
- .modal-content {
97
- background-color: #fefefe;
98
- padding-bottom: 15px;
99
- border: 1px solid #888;
100
- display: inline-block;
101
- min-width: 200px;
102
- }
103
- CSS
104
- # .close {
105
- # color: #aaaaaa;
106
- # float: right;
107
- # font-weight: bold;
108
- # margin: 5px;
109
- # }
110
- # .close:hover,
111
- # .close:focus {
112
- # color: #000;
113
- # text-decoration: none;
114
- # cursor: pointer;
115
- # }
116
- end
117
-
118
112
  def dom
119
- modal_id = id
120
- modal_style = css
121
- modal_text = text
122
- modal_message = message
123
- modal_class = ['modal', name].join(' ')
124
113
  @dom ||= html {
125
- div(id: modal_id, style: modal_style, class: modal_class) {
126
- style(class: 'modal-style') {
127
- style_dom_modal_css #.split("\n").map(&:strip).join(' ')
128
- }
114
+ div(id: id, class: "modal #{name}") {
129
115
  div(class: 'modal-content') {
130
116
  header(class: 'text') {
131
- modal_text
117
+ text
132
118
  }
133
119
  tag(_name: 'p', id: 'message', class: 'message') {
134
- modal_message
120
+ html_message
135
121
  }
136
122
  input(type: 'button', class: 'close', autofocus: 'autofocus', value: 'OK')
137
123
  }