glimmer-dsl-opal 0.9.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +213 -104
  4. data/VERSION +1 -1
  5. data/lib/display.rb +1 -1
  6. data/lib/glimmer-dsl-opal.rb +4 -3
  7. data/lib/glimmer-dsl-opal/ext/file.rb +1 -1
  8. data/lib/glimmer-dsl-opal/ext/glimmer/dsl/engine.rb +27 -2
  9. data/lib/glimmer-dsl-opal/samples/elaborate/contact_manager/contact_repository.rb +4 -4
  10. data/lib/glimmer-dsl-opal/samples/elaborate/tic_tac_toe.rb +2 -2
  11. data/lib/glimmer-dsl-opal/samples/hello/hello_browser.rb +1 -1
  12. data/lib/glimmer-dsl-opal/samples/hello/hello_button.rb +1 -1
  13. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox.rb +1 -1
  14. data/lib/glimmer-dsl-opal/samples/hello/hello_checkbox_group.rb +1 -1
  15. data/lib/glimmer-dsl-opal/samples/hello/hello_combo.rb +1 -1
  16. data/lib/glimmer-dsl-opal/samples/hello/hello_computed.rb +1 -1
  17. data/lib/glimmer-dsl-opal/samples/hello/hello_computed/contact.rb +21 -0
  18. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_shell.rb +1 -1
  19. data/lib/glimmer-dsl-opal/samples/hello/hello_custom_widget.rb +1 -1
  20. data/lib/glimmer-dsl-opal/samples/hello/hello_date_time.rb +1 -1
  21. data/lib/glimmer-dsl-opal/samples/hello/hello_dialog.rb +78 -0
  22. data/lib/glimmer-dsl-opal/samples/hello/hello_group.rb +1 -1
  23. data/lib/glimmer-dsl-opal/samples/hello/hello_list_multi_selection.rb +1 -1
  24. data/lib/glimmer-dsl-opal/samples/hello/hello_list_single_selection.rb +1 -1
  25. data/lib/glimmer-dsl-opal/samples/hello/hello_menu_bar.rb +1 -1
  26. data/lib/glimmer-dsl-opal/samples/hello/hello_message_box.rb +1 -1
  27. data/lib/glimmer-dsl-opal/samples/hello/hello_pop_up_context_menu.rb +1 -1
  28. data/lib/glimmer-dsl-opal/samples/hello/hello_radio.rb +1 -1
  29. data/lib/glimmer-dsl-opal/samples/hello/hello_radio_group.rb +1 -1
  30. data/lib/glimmer-dsl-opal/samples/hello/hello_tab.rb +1 -1
  31. data/lib/glimmer-dsl-opal/samples/hello/hello_table.rb +1 -1
  32. data/lib/glimmer-dsl-opal/samples/hello/hello_world.rb +1 -1
  33. data/lib/glimmer-dsl-swt.rb +1 -1
  34. data/lib/glimmer/dsl/opal/async_exec_expression.rb +1 -1
  35. data/lib/glimmer/dsl/opal/block_property_expression.rb +1 -1
  36. data/lib/glimmer/dsl/opal/checkbox_group_selection_data_binding_expression.rb +1 -1
  37. data/lib/glimmer/dsl/opal/color_expression.rb +1 -1
  38. data/lib/glimmer/dsl/opal/custom_widget_expression.rb +1 -1
  39. data/lib/glimmer/dsl/opal/dialog_expression.rb +20 -0
  40. data/lib/glimmer/dsl/opal/display_expression.rb +1 -1
  41. data/lib/glimmer/dsl/opal/dsl.rb +1 -1
  42. data/lib/glimmer/dsl/opal/exec_expression.rb +1 -1
  43. data/lib/glimmer/dsl/opal/font_expression.rb +1 -1
  44. data/lib/glimmer/dsl/opal/menu_bar_expression.rb +1 -1
  45. data/lib/glimmer/dsl/opal/menu_expression.rb +1 -1
  46. data/lib/glimmer/dsl/opal/message_box_expression.rb +1 -1
  47. data/lib/glimmer/dsl/opal/radio_group_selection_data_binding_expression.rb +1 -1
  48. data/lib/glimmer/dsl/opal/rgb_expression.rb +1 -1
  49. data/lib/glimmer/dsl/opal/rgba_expression.rb +1 -1
  50. data/lib/glimmer/dsl/opal/swt_expression.rb +4 -4
  51. data/lib/glimmer/dsl/opal/sync_exec_expression.rb +1 -1
  52. data/lib/glimmer/swt.rb +1 -1
  53. data/lib/glimmer/swt/color_proxy.rb +1 -1
  54. data/lib/glimmer/swt/composite_proxy.rb +16 -2
  55. data/lib/glimmer/swt/control_editor.rb +1 -1
  56. data/lib/glimmer/swt/custom/checkbox_group.rb +1 -1
  57. data/lib/glimmer/swt/custom/radio_group.rb +1 -1
  58. data/lib/glimmer/swt/dialog_proxy.rb +188 -0
  59. data/lib/glimmer/swt/display_proxy.rb +70 -10
  60. data/lib/glimmer/swt/font_proxy.rb +1 -1
  61. data/lib/glimmer/swt/grid_layout_proxy.rb +11 -16
  62. data/lib/glimmer/swt/label_proxy.rb +22 -1
  63. data/lib/{glimmer-dsl-opal/ext/struct.rb → glimmer/swt/latest_dialog_proxy.rb} +19 -12
  64. data/lib/glimmer/swt/latest_message_box_proxy.rb +25 -1
  65. data/lib/glimmer/swt/latest_shell_proxy.rb +43 -4
  66. data/lib/glimmer/swt/layout_data_proxy.rb +4 -0
  67. data/lib/glimmer/swt/layout_proxy.rb +9 -6
  68. data/lib/glimmer/swt/make_shift_shell_proxy.rb +1 -1
  69. data/lib/glimmer/swt/menu_item_proxy.rb +1 -1
  70. data/lib/glimmer/swt/menu_proxy.rb +3 -3
  71. data/lib/glimmer/swt/message_box_proxy.rb +45 -16
  72. data/lib/glimmer/swt/row_layout_proxy.rb +24 -6
  73. data/lib/glimmer/swt/shell_proxy.rb +43 -9
  74. data/lib/glimmer/swt/style_constantizable.rb +1 -1
  75. data/lib/glimmer/swt/swt_proxy.rb +1 -1
  76. data/lib/glimmer/swt/tab_folder_proxy.rb +1 -1
  77. data/lib/glimmer/swt/table_editor.rb +1 -1
  78. data/lib/glimmer/swt/table_item_proxy.rb +1 -1
  79. data/lib/glimmer/swt/table_proxy.rb +1 -1
  80. data/lib/glimmer/swt/widget_proxy.rb +50 -17
  81. data/lib/glimmer/ui/custom_shell.rb +1 -1
  82. data/lib/glimmer/ui/custom_widget.rb +1 -1
  83. data/lib/glimmer/util/proc_tracker.rb +17 -6
  84. data/lib/net/http.rb +30 -2
  85. data/lib/os.rb +1 -1
  86. metadata +28 -7
  87. data/CHANGELOG.md +0 -194
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Andy Maleh
1
+ # Copyright (c) 2020-2021 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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Andy Maleh
1
+ # Copyright (c) 2020-2021 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
@@ -4,8 +4,6 @@ require 'glimmer/swt/widget_proxy'
4
4
  module Glimmer
5
5
  module SWT
6
6
  class CompositeProxy < WidgetProxy
7
- attr_reader :layout
8
-
9
7
  def initialize(parent, args, block)
10
8
  super(parent, args, block)
11
9
  @layout = default_layout
@@ -27,7 +25,23 @@ module Glimmer
27
25
  def layout=(the_layout)
28
26
  @layout = the_layout
29
27
  end
28
+ alias set_layout layout=
29
+ alias setLayout layout=
30
+
31
+ def get_layout
32
+ @layout
33
+ end
34
+ alias getLayout get_layout #TODO consider pregenerating these aliases with an easy method in the future
35
+
36
+ def pack(*args)
37
+ # No Op (just a shim) TODO consider if it should be implemented
38
+ end
30
39
 
40
+ def layout​(changed = nil, all = nil)
41
+ # TODO implement layout(changed = nil, all = nil) just as per SWT API
42
+ @layout&.layout(self, changed)
43
+ end
44
+
31
45
  end
32
46
 
33
47
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Andy Maleh
1
+ # Copyright (c) 2020-2021 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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Andy Maleh
1
+ # Copyright (c) 2020-2021 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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Andy Maleh
1
+ # Copyright (c) 2020-2021 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
@@ -0,0 +1,188 @@
1
+ # Copyright (c) 2020-2021 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/display_proxy'
24
+
25
+ module Glimmer
26
+ module SWT
27
+ class DialogProxy < CompositeProxy
28
+ STYLE = <<~CSS
29
+ .ui-dialog .ui-dialog-content {
30
+ background: rgb(235, 235, 235);
31
+ }
32
+ .ui-dialog-titlebar {
33
+ background: rgb(80, 116, 211);
34
+ color: white;
35
+ }
36
+ .ui-dialog .dialog .ui-widget-overlay {
37
+ z-index: 10 !important;
38
+ background-color: rgba(0, 0, 0, 0.4);
39
+ opacity: 1;
40
+ }
41
+ .ui-dialog * {
42
+ z-index: 200 !important;
43
+ }
44
+ CSS
45
+
46
+
47
+ # .close {
48
+ # color: #aaaaaa;
49
+ # float: right;
50
+ # font-weight: bold;
51
+ # margin: 5px;
52
+ # }
53
+ # .close:hover,
54
+ # .close:focus {
55
+ # color: #000;
56
+ # text-decoration: none;
57
+ # cursor: pointer;
58
+ # }
59
+
60
+ attr_reader :text
61
+
62
+ def initialize(parent, args, block)
63
+ i = 0
64
+ @parent = parent
65
+ @parent = nil if parent.is_a?(LatestShellProxy)
66
+ @parent ||= DisplayProxy.instance.shells.detect(&:open?) || ShellProxy.new([])
67
+ @args = args
68
+ @block = block
69
+ @children = Set.new
70
+ @enabled = true
71
+ DisplayProxy.instance.opened_dialogs.last&.suspend_event_handling
72
+ DisplayProxy.instance.dialogs << self
73
+ @parent.post_initialize_child(self)
74
+ end
75
+
76
+ def text=(txt)
77
+ @text = txt
78
+ if @init
79
+ dom_element.dialog('option', 'title', @text)
80
+ else
81
+ dom_element.attr('title', @text)
82
+ end
83
+ end
84
+
85
+ def open?
86
+ @open
87
+ end
88
+
89
+ def open
90
+ owned_proc = Glimmer::Util::ProcTracker.new(owner: self, invoked_from: :open) {
91
+ shell.open(async: false) unless shell.open?
92
+ unless @init
93
+ dom_element.remove_class('hide')
94
+ dom_element.dialog('auto_open' => false)
95
+ @init = true
96
+ dom_element.dialog('option', 'appendTo', parent.path)
97
+ dom_element.dialog('option', 'modal', true) # NOTE: Not Working! Doing manually below by relying on overlay in ShellProxy.
98
+ unless DisplayProxy.instance.dialogs.any?(&:open?) # only add for first dialog open
99
+ Element['.dialog-overlay'].remove_class('hide')
100
+ end
101
+ dom_element.dialog('option', 'closeOnEscape', true)
102
+ dom_element.dialog('option', 'draggable', true)
103
+ dom_element.dialog('option', 'width', 'auto')
104
+ dom_element.dialog('option', 'minHeight', 'none')
105
+ dom_element.on('dialogclose') do
106
+ unless @hiding
107
+ close
108
+ else
109
+ @hiding = false
110
+ end
111
+ end
112
+ else
113
+ dom_element.dialog('open')
114
+ end
115
+ @open = true
116
+ }
117
+ DisplayProxy.instance.async_exec(owned_proc)
118
+ end
119
+
120
+ def hide
121
+ @hiding = true
122
+ dom_element.dialog('close')
123
+ @open = false
124
+ Element['.dialog-overlay'].add_class('hide') unless DisplayProxy.instance.dialogs.any?(&:open?)
125
+ end
126
+
127
+ def close
128
+ dom_element.dialog('destroy')
129
+ dom_element.remove
130
+ @open = false
131
+ @init = false
132
+ Element['.dialog-overlay'].add_class('hide') unless DisplayProxy.instance.dialogs.any?(&:open?)
133
+ parent.children.delete(self)
134
+ shell.close if shell.children.empty?
135
+ DisplayProxy.instance.dialogs.delete(self)
136
+ DisplayProxy.instance.opened_dialogs.last&.resume_event_handling
137
+ end
138
+
139
+
140
+ def content(&block)
141
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Opal::DialogExpression.new, &block)
142
+ end
143
+
144
+ def path
145
+ if @init # it gets moved once initialized by jQuery UI, so only ID is reliable then
146
+ "##{id}"
147
+ else
148
+ super
149
+ end
150
+ end
151
+
152
+ def suspend_event_handling
153
+ super
154
+ Element["[aria-describedby=#{id}]"].css('z-index', 9)
155
+ end
156
+
157
+ def resume_event_handling
158
+ super
159
+ Element["[aria-describedby=#{id}]"].css('z-index', 100)
160
+ end
161
+
162
+ # def selector
163
+ # super + ' .close'
164
+ # end
165
+ #
166
+ # def listener_path
167
+ # widget_path + ' .close'
168
+ # end
169
+ #
170
+ # def observation_request_to_event_mapping
171
+ # {
172
+ # 'on_widget_selected' => {
173
+ # event: 'click'
174
+ # },
175
+ # }
176
+ # end
177
+
178
+ def dom
179
+ @dom ||= html {
180
+ div(id: id, class: "#{name} modal hide", title: text) {
181
+ }
182
+ }.to_s
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ require 'glimmer/dsl/opal/dialog_expression'
@@ -1,3 +1,5 @@
1
+ require 'glimmer/swt/widget_proxy'
2
+
1
3
  module Glimmer
2
4
  module SWT
3
5
  class DisplayProxy < WidgetProxy
@@ -31,22 +33,46 @@ module Glimmer
31
33
  def message_boxes
32
34
  @message_boxes ||= []
33
35
  end
34
- alias message_boxs message_boxes # alias for automating processing as plural from keyword by adding s only
36
+
37
+ def dialogs
38
+ @dialogs ||= []
39
+ end
40
+
41
+ def modals
42
+ message_boxes + dialogs
43
+ end
44
+
45
+ def message_box_open?
46
+ message_boxes.any?(&:open?)
47
+ end
48
+
49
+ def dialog_open?
50
+ dialogs.any?(&:open?)
51
+ end
52
+
53
+ def opened_dialogs
54
+ dialogs.select(&:open?)
55
+ end
56
+
57
+ def modal_open?
58
+ message_box_open? or dialog_open?
59
+ end
35
60
 
36
61
  def render
37
62
  # No rendering as body is rendered as part of ShellProxy.. this class only serves as an SWT Display utility
38
63
  end
39
64
 
40
- def async_exec(&block)
41
- executer = lambda do
42
- if Document.find('.modal').to_a.empty?
43
- block.call
44
- else
45
- sleep(0.05)
46
- Async::Task.new(&executer)
47
- end
65
+ def async_exec(proc_tracker = nil, &block)
66
+ block = proc_tracker unless proc_tracker.nil?
67
+ queue = nil # general queue
68
+ if !proc_tracker.nil? && proc_tracker.invoked_from.to_s == 'open' && modal_open? &&
69
+ (
70
+ proc_tracker.owner.is_a?(MessageBoxProxy) ||
71
+ (dialog_open? && proc_tracker.owner.is_a?(DialogProxy) && opened_dialogs.last == WidgetProxy.widget_handling_listener&.dialog_ancestor)
72
+ )
73
+ queue = WidgetProxy.widget_handling_listener
48
74
  end
49
- Async::Task.new(&executer)
75
+ schedule_async_exec(block, queue)
50
76
  end
51
77
  # sync_exec kept for API compatibility reasons
52
78
  alias sync_exec async_exec
@@ -79,6 +105,40 @@ module Glimmer
79
105
  ]
80
106
  }
81
107
  end
108
+
109
+ private
110
+
111
+ def async_exec_queues
112
+ @async_exec_queues ||= {}
113
+ end
114
+
115
+ def async_exec_queue(widget_handling_listener = nil)
116
+ async_exec_queues[widget_handling_listener] ||= []
117
+ end
118
+
119
+ def no_widget_handling_listener_work?
120
+ async_exec_queues.reject {|key, value| key.nil?}.values.reduce(:+).to_a.empty?
121
+ end
122
+
123
+ def schedule_async_exec(block, queue)
124
+ async_exec_queue(queue).unshift(block)
125
+
126
+ # TODO consider the need for locking to avoid race conditions (rare or impossible case)
127
+ if async_exec_queue(queue).size == 1
128
+ executer = lambda do
129
+ # queue could be a widget handling listener queue
130
+ # TODO see if there are more intricate cases of opening a dialog from a widget listener handler
131
+ if !message_box_open? && (!dialog_open? || queue&.dialog_ancestor == opened_dialogs.last) && ((!queue.nil? && async_exec_queues.keys.last == queue) || no_widget_handling_listener_work?)
132
+ block = async_exec_queue(queue).pop
133
+ block.call
134
+ Async::Task.new(delay: 1, &executer) if async_exec_queue(queue).any?
135
+ else
136
+ Async::Task.new(delay: 100, &executer)
137
+ end
138
+ end
139
+ Async::Task.new(delay: 1, &executer)
140
+ end
141
+ end
82
142
  end
83
143
  end
84
144
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Andy Maleh
1
+ # Copyright (c) 2020-2021 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
@@ -14,39 +14,29 @@ module Glimmer
14
14
 
15
15
  attr_reader :num_columns, :make_columns_equal_width, :horizontal_spacing, :vertical_spacing, :margin_width, :margin_height
16
16
 
17
- def initialize(parent, args)
18
- super(parent, args)
19
- self.horizontal_spacing = 10
20
- self.vertical_spacing = 10
21
- self.margin_width = 15
22
- self.margin_height = 15
23
- self.num_columns = @args.first || 1
24
- reapply
25
- end
26
-
27
17
  def num_columns=(columns)
28
18
  @num_columns = columns
29
19
  # TODO do the following instead of reapply
30
20
  # @parent.add_css_class("num-columns-#{@num_columns}")
31
- reapply
21
+ # reinitialize # TODO reimplement without using reinitialize
32
22
  end
33
23
 
34
24
  def make_columns_equal_width=(equal_width)
35
25
  @make_columns_equal_width = equal_width
36
26
  # @parent.add_css_class('make_columns_equal_width') if @make_columns_equal_width
37
- reapply
27
+ # reinitialize # TODO reimplement without using reinitialize
38
28
  end
39
29
 
40
30
  def horizontal_spacing=(spacing)
41
31
  @horizontal_spacing = spacing
42
32
  # @parent.add_css_class("horizontal-spacing-#{@horizontal_spacing}")
43
- reapply
33
+ # reinitialize # TODO reimplement without using reinitialize
44
34
  end
45
35
 
46
36
  def vertical_spacing=(spacing)
47
37
  @vertical_spacing = spacing
48
38
  # @parent.add_css_class("vertical-spacing-#{@vertical_spacing}")
49
- reapply
39
+ # reinitialize # TODO reimplement without using reinitialize
50
40
  end
51
41
 
52
42
  def margin_width=(pixels)
@@ -66,8 +56,13 @@ module Glimmer
66
56
  @parent.dom_element.css('padding-bottom', effective_margin_height)
67
57
  end
68
58
 
69
- def reapply
70
- # TODO get rid of this method
59
+ def initialize(parent, args)
60
+ super(parent, args)
61
+ self.horizontal_spacing = 10
62
+ self.vertical_spacing = 10
63
+ self.margin_width = 15
64
+ self.margin_height = 15
65
+ self.num_columns = @args.first || 1
71
66
  layout_css = <<~CSS
72
67
  grid-template-columns: #{'auto ' * @num_columns.to_i};
73
68
  grid-row-gap: #{@vertical_spacing}px;
@@ -1,3 +1,24 @@
1
+ # Copyright (c) 2020-2021 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/image_proxy'
3
24
 
@@ -16,7 +37,7 @@ module Glimmer
16
37
  end
17
38
 
18
39
  def html_text
19
- text.gsub("\n", '<br />')
40
+ text&.gsub("\n", '<br />')
20
41
  end
21
42
 
22
43
  def background_image=(*image_options)