glimmer-dsl-swt 4.18.7.6 → 4.20.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/README.md +34 -21
  4. data/RUBY_VERSION +1 -1
  5. data/VERSION +1 -1
  6. data/bin/girb +10 -9
  7. data/bin/girb_runner.rb +8 -3
  8. data/bin/glimmer +10 -1
  9. data/bin/glimmer-setup +58 -0
  10. data/bin/glimmer_runner.rb +4 -0
  11. data/docs/reference/GLIMMER_COMMAND.md +19 -30
  12. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +39 -10
  13. data/docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md +7 -1
  14. data/docs/reference/GLIMMER_SAMPLES.md +27 -0
  15. data/glimmer-dsl-swt.gemspec +0 -0
  16. data/lib/glimmer-dsl-swt.rb +8 -0
  17. data/lib/glimmer/data_binding/shine.rb +41 -26
  18. data/lib/glimmer/data_binding/widget_binding.rb +1 -1
  19. data/lib/glimmer/dsl/swt/c_tab_item_expression.rb +58 -0
  20. data/lib/glimmer/dsl/swt/combo_selection_data_binding_expression.rb +2 -1
  21. data/lib/glimmer/dsl/swt/dsl.rb +2 -1
  22. data/lib/glimmer/dsl/swt/shine_data_binding_expression.rb +49 -0
  23. data/lib/glimmer/dsl/swt/tab_item_expression.rb +7 -3
  24. data/lib/glimmer/dsl/swt/widget_expression.rb +1 -1
  25. data/lib/glimmer/launcher.rb +33 -51
  26. data/lib/glimmer/rake_task.rb +1 -1
  27. data/lib/glimmer/rake_task/package.rb +7 -2
  28. data/lib/glimmer/rake_task/scaffold.rb +227 -254
  29. data/lib/glimmer/swt/c_tab_item_proxy.rb +53 -0
  30. data/lib/glimmer/swt/custom/code_text.rb +17 -9
  31. data/lib/glimmer/swt/custom/shape.rb +15 -1
  32. data/lib/glimmer/swt/sash_form_proxy.rb +6 -0
  33. data/lib/glimmer/swt/shell_proxy.rb +3 -1
  34. data/lib/glimmer/swt/tab_folder_proxy.rb +1 -0
  35. data/lib/glimmer/swt/tab_item_proxy.rb +17 -18
  36. data/lib/glimmer/swt/widget_proxy.rb +9 -0
  37. data/samples/elaborate/contact_manager.rb +7 -5
  38. data/samples/elaborate/login.rb +7 -7
  39. data/samples/elaborate/mandelbrot_fractal.rb +13 -8
  40. data/samples/elaborate/meta_sample.rb +4 -2
  41. data/samples/elaborate/metronome.rb +4 -4
  42. data/samples/elaborate/stock_ticker.rb +2 -2
  43. data/samples/elaborate/tic_tac_toe.rb +2 -2
  44. data/samples/hello/hello_button.rb +1 -1
  45. data/samples/hello/hello_c_combo.rb +69 -0
  46. data/samples/hello/hello_c_tab.rb +174 -0
  47. data/samples/hello/hello_c_tab/denmark.png +0 -0
  48. data/samples/hello/hello_c_tab/finland.png +0 -0
  49. data/samples/hello/hello_c_tab/france.png +0 -0
  50. data/samples/hello/hello_c_tab/germany.png +0 -0
  51. data/samples/hello/hello_c_tab/italy.png +0 -0
  52. data/samples/hello/hello_c_tab/mexico.png +0 -0
  53. data/samples/hello/hello_c_tab/netherlands.png +0 -0
  54. data/samples/hello/hello_c_tab/norway.png +0 -0
  55. data/samples/hello/hello_c_tab/usa.png +0 -0
  56. data/samples/hello/hello_canvas.rb +5 -5
  57. data/samples/hello/hello_canvas_animation_data_binding.rb +1 -1
  58. data/samples/hello/hello_canvas_data_binding.rb +16 -16
  59. data/samples/hello/hello_checkbox.rb +4 -4
  60. data/samples/hello/hello_code_text.rb +3 -57
  61. data/samples/hello/hello_color_dialog.rb +1 -1
  62. data/samples/hello/hello_combo.rb +1 -1
  63. data/samples/hello/hello_computed.rb +5 -5
  64. data/samples/hello/hello_custom_widget.rb +1 -1
  65. data/samples/hello/hello_date_time.rb +4 -4
  66. data/samples/hello/hello_dialog.rb +3 -2
  67. data/samples/hello/hello_drag_and_drop.rb +1 -1
  68. data/samples/hello/hello_file_dialog.rb +1 -1
  69. data/samples/hello/hello_font_dialog.rb +3 -3
  70. data/samples/hello/hello_group.rb +6 -6
  71. data/samples/hello/hello_link.rb +56 -50
  72. data/samples/hello/hello_list_multi_selection.rb +1 -1
  73. data/samples/hello/hello_list_single_selection.rb +1 -1
  74. data/samples/hello/hello_progress_bar.rb +10 -10
  75. data/samples/hello/hello_radio.rb +6 -6
  76. data/samples/hello/hello_sash_form.rb +4 -4
  77. data/samples/hello/hello_shape.rb +1 -0
  78. data/samples/hello/hello_spinner.rb +6 -2
  79. data/samples/hello/hello_styled_text.rb +11 -11
  80. data/samples/hello/hello_tab.rb +2 -0
  81. data/vendor/swt/linux/swt.jar +0 -0
  82. data/vendor/swt/linux_aarch64/swt.jar +0 -0
  83. data/vendor/swt/mac/swt.jar +0 -0
  84. data/vendor/swt/mac_aarch64/swt.jar +0 -0
  85. data/vendor/swt/windows/swt.jar +0 -0
  86. metadata +76 -32
@@ -0,0 +1,53 @@
1
+ # Copyright (c) 2007-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/tab_item_proxy'
24
+
25
+ module Glimmer
26
+ module SWT
27
+ # Proxy for org.eclipse.swt.custom.CTabItem
28
+ #
29
+ # Functions differently from other widget proxies.
30
+ #
31
+ # Glimmer instantiates an SWT Composite alongside the SWT TabItem
32
+ # and returns it for `#swt_widget` to allow adding widgets into it.
33
+ #
34
+ # In order to get the SWT TabItem object, one must call `#swt_tab_item`.
35
+ #
36
+ # Behind the scenes, this creates a tab item widget proxy separately from a composite that
37
+ # is set as the control of the tab item and `#swt_widget`.
38
+ #
39
+ # In order to retrieve the tab item widget proxy, one must call `#widget_proxy`
40
+ #
41
+ # Follows the Proxy Design Pattern
42
+ class CTabItemProxy < TabItemProxy
43
+ ATTRIBUTES = TabItemProxy::ATTRIBUTES + %w[foreground selection_foreground show_close font]
44
+
45
+ def attributes
46
+ ATTRIBUTES
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -78,6 +78,8 @@ module Glimmer
78
78
 
79
79
  def can_handle_observation_request?(observation_request)
80
80
  @styled_text_proxy&.can_handle_observation_request?(observation_request) || super
81
+ rescue
82
+ super
81
83
  end
82
84
 
83
85
  def handle_observation_request(observation_request, &block)
@@ -86,6 +88,8 @@ module Glimmer
86
88
  else
87
89
  super
88
90
  end
91
+ rescue
92
+ super
89
93
  end
90
94
 
91
95
  def root_block=(block)
@@ -120,21 +124,15 @@ module Glimmer
120
124
 
121
125
  @line_numbers_styled_text_proxy = styled_text(swt(swt(swt_style), :h_scroll!, :v_scroll!)) {
122
126
  layout_data(:right, :fill, false, true)
123
- top_pixel_before_read = nil
127
+
124
128
  text bind(self,
125
129
  :styled_text_proxy_text,
126
130
  read_only: true,
127
131
  on_read: lambda { |text_value|
128
- line_count = "#{text_value} ".split("\n").count
129
- line_count = 1 if line_count == 0
130
- lines_text_size = [line_count.to_s.size, @lines_width].max
131
- if lines_text_size > @lines_width
132
- @lines_width = lines_text_size
133
- end
134
- line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n") + "\n"
132
+ line_numbers_text_from(text_value)
135
133
  },
136
134
  after_read: lambda {
137
- @line_numbers_styled_text_proxy&.top_pixel = styled_text_proxy_top_pixel
135
+ @line_numbers_styled_text_proxy&.top_pixel = styled_text_proxy_top_pixel unless styled_text_proxy_top_pixel.nil?
138
136
  }
139
137
  )
140
138
  top_pixel bind(self, :styled_text_proxy_top_pixel, read_only: true)
@@ -147,6 +145,7 @@ module Glimmer
147
145
  left_margin 5
148
146
  editable false
149
147
  caret nil
148
+
150
149
  on_focus_gained {
151
150
  @styled_text_proxy&.setFocus
152
151
  }
@@ -169,6 +168,7 @@ module Glimmer
169
168
  @styled_text_proxy = styled_text(swt_style) {
170
169
  # custom_widget_property_owner # TODO implement to route properties here without declaring method_missing
171
170
  layout_data :fill, :fill, true, true if lines
171
+
172
172
  text bind(self, :styled_text_proxy_text) if lines
173
173
  top_pixel bind(self, :styled_text_proxy_top_pixel) if lines
174
174
  font name: @font_name, height: OS.mac? ? 15 : 12
@@ -287,6 +287,14 @@ module Glimmer
287
287
  new_offset = beginning_of_current_line_offset + current_line.size
288
288
  @styled_text_proxy.setSelection(new_offset, new_offset)
289
289
  end
290
+
291
+ def line_numbers_text_from(text_value)
292
+ line_count = "#{text_value} ".split("\n").count
293
+ line_count = 1 if line_count == 0
294
+ lines_text_size = [line_count.to_s.size, @lines_width].max
295
+ @lines_width = lines_text_size if lines_text_size > @lines_width
296
+ line_count.times.map {|n| (' ' * (lines_text_size - (n+1).to_s.size)) + (n+1).to_s }.join("\n") + "\n"
297
+ end
290
298
  end
291
299
  end
292
300
  end
@@ -572,7 +572,7 @@ module Glimmer
572
572
  end
573
573
 
574
574
  def dispose(dispose_images: true, dispose_patterns: true, redraw: true)
575
- shapes.each { |shape| shape.is_a?(Shape::Path) && shape.dispose }
575
+ shapes.each { |shape| shape.is_a?(Shape::Path) && shape.dispose } # TODO look into why I'm only disposing paths
576
576
  if dispose_patterns
577
577
  @background_pattern&.dispose
578
578
  @background_pattern = nil
@@ -587,6 +587,20 @@ module Glimmer
587
587
  drawable.redraw if redraw && !drawable.is_a?(ImageProxy)
588
588
  end
589
589
 
590
+ # clear all shapes
591
+ # indicate whether to dispose images, dispose patterns, and redraw after clearing shapes.
592
+ # redraw can be `:all` or `true` to mean redraw after all shapes are disposed, `:each` to mean redraw after each shape is disposed, or `false` to avoid redraw altogether
593
+ def clear_shapes(dispose_images: true, dispose_patterns: true, redraw: :all)
594
+ if redraw == true || redraw == :all
595
+ shapes.dup.each {|shape| shape.dispose(dispose_images: dispose_images, dispose_patterns: dispose_patterns, redraw: false) }
596
+ drawable.redraw if redraw && !drawable.is_a?(ImageProxy)
597
+ elsif redraw == :each
598
+ shapes.dup.each {|shape| shape.dispose(dispose_images: dispose_images, dispose_patterns: dispose_patterns, redraw: true) }
599
+ else
600
+ shapes.dup.each {|shape| shape.dispose(dispose_images: dispose_images, dispose_patterns: dispose_patterns, redraw: false) }
601
+ end
602
+ end
603
+
590
604
  # Indicate if this is a container shape (meaning a shape bag that is just there to contain nested shapes, but doesn't render anything of its own)
591
605
  def container?
592
606
  @name == 'shape'
@@ -20,6 +20,7 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'glimmer/swt/widget_proxy'
23
+ require 'glimmer/swt/color_proxy'
23
24
 
24
25
  module Glimmer
25
26
  module SWT
@@ -27,6 +28,11 @@ module Glimmer
27
28
  #
28
29
  # Follows the Proxy Design Pattern
29
30
  class SashFormProxy < WidgetProxy
31
+ def initialize(*args, &block)
32
+ super
33
+ self.background = ColorProxy.new(230, 230, 230).swt_color
34
+ end
35
+
30
36
  def post_add_content
31
37
  self.weights = @weights unless @weights.nil?
32
38
  end
@@ -74,7 +74,9 @@ module Glimmer
74
74
  # TODO make this an option not the default
75
75
  shell_swt_display = Glimmer::SWT::DisplayProxy.instance.swt_display
76
76
  on_swt_show do
77
- @swt_widget.set_size(@display.bounds.width, @display.bounds.height) if fill_screen
77
+ if @filled_screen.nil? && fill_screen # only the first time
78
+ @swt_widget.set_size(@display.bounds.width, @display.bounds.height)
79
+ end
78
80
  Thread.new do
79
81
  sleep(0.25)
80
82
  shell_swt_display.async_exec do
@@ -48,5 +48,6 @@ module Glimmer
48
48
  end
49
49
  end
50
50
  end
51
+ CTabFolderProxy = TabFolderProxy
51
52
  end
52
53
  end
@@ -39,44 +39,40 @@ module Glimmer
39
39
  #
40
40
  # Follows the Proxy Design Pattern
41
41
  class TabItemProxy < WidgetProxy
42
- ATTRIBUTES = %w[text image]
42
+ ATTRIBUTES = %w[text image tool_tip_text]
43
43
  include_package 'org.eclipse.swt.widgets'
44
44
 
45
45
  attr_reader :widget_proxy, :swt_tab_item
46
46
 
47
47
  def initialize(parent, style, &contents)
48
48
  super("composite", parent, style, &contents)
49
- @widget_proxy = SWT::WidgetProxy.new('tab_item', parent, style)
49
+ keyword = self.class.name.split('::').last.underscore.sub('_proxy', '')
50
+ @widget_proxy = SWT::WidgetProxy.new(keyword, parent, style)
50
51
  @swt_tab_item = @widget_proxy.swt_widget
51
52
  @swt_tab_item.control = swt_widget
52
- # parent.pack # TODO auto fix the tab folder to avoid having to do this manually (including maintaining bounds and minimum size of shell)
53
+ end
54
+
55
+ def attributes
56
+ ATTRIBUTES
53
57
  end
54
58
 
55
59
  def has_attribute?(attribute_name, *args)
56
- if ATTRIBUTES.include?(attribute_name.to_s)
57
- true
58
- else
59
- super(attribute_name, *args)
60
- end
60
+ attributes.include?(attribute_name.to_s) || super(attribute_name, *args)
61
61
  end
62
62
 
63
63
  def set_attribute(attribute_name, *args)
64
- attribute_name
65
- if attribute_name.to_s == "text"
66
- text_value = args[0]
67
- @swt_tab_item.setText text_value
68
- elsif attribute_name.to_s == "image"
69
- widget_proxy.set_attribute('image', *args)
64
+ attribute_name = attribute_name.to_s
65
+ if attributes.include?(attribute_name)
66
+ widget_proxy.set_attribute(attribute_name, *args)
70
67
  else
71
68
  super(attribute_name, *args)
72
69
  end
73
70
  end
74
71
 
75
72
  def get_attribute(attribute_name)
76
- if attribute_name.to_s == "text"
77
- @swt_tab_item.getText
78
- elsif attribute_name.to_s == "image"
79
- widget_proxy.get_attribute('image')
73
+ attribute_name = attribute_name.to_s
74
+ if attributes.include?(attribute_name)
75
+ widget_proxy.get_attribute(attribute_name)
80
76
  else
81
77
  super(attribute_name)
82
78
  end
@@ -89,6 +85,9 @@ module Glimmer
89
85
  swt_tab_item.dispose
90
86
  end
91
87
  end
88
+
92
89
  end
90
+
93
91
  end
92
+
94
93
  end
@@ -51,6 +51,7 @@ module Glimmer
51
51
  'arrow' => [:arrow],
52
52
  'button' => [:push],
53
53
  'canvas' => ([:double_buffered] unless OS.mac?),
54
+ 'ccombo' => [:border],
54
55
  'checkbox' => [:check],
55
56
  'check' => [:check],
56
57
  'drag_source' => [:drop_copy],
@@ -339,6 +340,13 @@ module Glimmer
339
340
  }
340
341
  end,
341
342
  },
343
+ Java::OrgEclipseSwtCustom::CCombo => {
344
+ :text => lambda do |observer|
345
+ on_modify_text { |modify_event|
346
+ observer.call(@swt_widget.getText)
347
+ }
348
+ end,
349
+ },
342
350
  Java::OrgEclipseSwtWidgets::Table => {
343
351
  :selection => lambda do |observer|
344
352
  on_widget_selected { |selection_event|
@@ -985,6 +993,7 @@ module Glimmer
985
993
  !!value
986
994
  end,
987
995
  foreground: color_converter,
996
+ selection_foreground: color_converter,
988
997
  link_foreground: color_converter,
989
998
  font: lambda do |value|
990
999
  if value.is_a?(Hash) || value.is_a?(FontData)
@@ -51,7 +51,7 @@ class ContactManager
51
51
  }
52
52
  text {
53
53
  layout_data :fill, :center, true, false
54
- text bind(@contact_manager_presenter, :first_name)
54
+ text <=> [@contact_manager_presenter, :first_name]
55
55
  on_key_pressed {|key_event|
56
56
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
57
57
  }
@@ -64,7 +64,7 @@ class ContactManager
64
64
  }
65
65
  text {
66
66
  layout_data :fill, :center, true, false
67
- text bind(@contact_manager_presenter, :last_name)
67
+ text <=> [@contact_manager_presenter, :last_name]
68
68
  on_key_pressed {|key_event|
69
69
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
70
70
  }
@@ -77,7 +77,7 @@ class ContactManager
77
77
  }
78
78
  text {
79
79
  layout_data :fill, :center, true, false
80
- text bind(@contact_manager_presenter, :email)
80
+ text <=> [@contact_manager_presenter, :email]
81
81
  on_key_pressed {|key_event|
82
82
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
83
83
  }
@@ -118,6 +118,7 @@ class ContactManager
118
118
  grab_excess_vertical_space true
119
119
  height_hint 200
120
120
  }
121
+
121
122
  table_column {
122
123
  text "First Name"
123
124
  width 80
@@ -130,8 +131,9 @@ class ContactManager
130
131
  text "Email"
131
132
  width 200
132
133
  }
133
- items bind(@contact_manager_presenter, :results),
134
- column_properties(:first_name, :last_name, :email)
134
+
135
+ items bind(@contact_manager_presenter, :results), column_properties(:first_name, :last_name, :email)
136
+
135
137
  on_mouse_up { |event|
136
138
  table_proxy.edit_table_item(event.table_item, event.column_index)
137
139
  }
@@ -82,8 +82,8 @@ class Login
82
82
 
83
83
  label { text "Username:" } # goes in column 1
84
84
  @user_name_text = text { # goes in column 2
85
- text bind(@presenter, :user_name)
86
- enabled bind(@presenter, :logged_out)
85
+ text <=> [@presenter, :user_name]
86
+ enabled <= [@presenter, :logged_out]
87
87
  on_key_pressed { |event|
88
88
  @password_text.set_focus if event.keyCode == swt(:cr)
89
89
  }
@@ -91,19 +91,19 @@ class Login
91
91
 
92
92
  label { text "Password:" }
93
93
  @password_text = text(:password, :border) {
94
- text bind(@presenter, :password)
95
- enabled bind(@presenter, :logged_out)
94
+ text <=> [@presenter, :password]
95
+ enabled <= [@presenter, :logged_out]
96
96
  on_key_pressed { |event|
97
97
  @presenter.login! if event.keyCode == swt(:cr)
98
98
  }
99
99
  }
100
100
 
101
101
  label { text "Status:" }
102
- label { text bind(@presenter, :status) }
102
+ label { text <= [@presenter, :status] }
103
103
 
104
104
  button {
105
105
  text "Login"
106
- enabled bind(@presenter, :logged_out)
106
+ enabled <= [@presenter, :logged_out]
107
107
  on_widget_selected { @presenter.login! }
108
108
  on_key_pressed { |event|
109
109
  @presenter.login! if event.keyCode == swt(:cr)
@@ -112,7 +112,7 @@ class Login
112
112
 
113
113
  button {
114
114
  text "Logout"
115
- enabled bind(@presenter, :logged_in)
115
+ enabled <= [@presenter, :logged_in]
116
116
  on_widget_selected { @presenter.logout! }
117
117
  on_key_pressed { |event|
118
118
  if event.keyCode == swt(:cr)
@@ -56,7 +56,7 @@ class Mandelbrot
56
56
  end
57
57
 
58
58
  def processor_count
59
- @processor_count ||= Concurrent.physical_processor_count
59
+ @processor_count ||= Concurrent.processor_count
60
60
  end
61
61
  end
62
62
 
@@ -103,7 +103,7 @@ class Mandelbrot
103
103
  puts "Points calculated already. Returning previously calculated points..."
104
104
  return @points
105
105
  end
106
- thread_pool = Concurrent::FixedThreadPool.new(Mandelbrot.processor_count, fallback_policy: :discard)
106
+ @thread_pool = Concurrent::FixedThreadPool.new(Mandelbrot.processor_count, fallback_policy: :discard)
107
107
  @points = Concurrent::Array.new(height)
108
108
  Mandelbrot.work_in_progress = "Calculating Mandelbrot Points for Zoom #{zoom}x"
109
109
  Mandelbrot.progress = 0
@@ -112,15 +112,15 @@ class Mandelbrot
112
112
  height.times do |y|
113
113
  @points[y] ||= Concurrent::Array.new(width)
114
114
  width.times do |x|
115
- thread_pool.post do
115
+ @thread_pool.post do
116
116
  @points[y][x] = calculate(x_array[x], y_array[y]).last
117
117
  point_index += 1
118
118
  Mandelbrot.progress += 1 if (point_index.to_f / point_count.to_f)*PROGRESS_MAX >= Mandelbrot.progress
119
119
  end
120
120
  end
121
121
  end
122
- thread_pool.shutdown
123
- thread_pool.wait_for_termination
122
+ @thread_pool.shutdown
123
+ @thread_pool.wait_for_termination
124
124
  Mandelbrot.progress = PROGRESS_MAX
125
125
  @points_calculated = true
126
126
  @points
@@ -164,7 +164,7 @@ class MandelbrotFractal
164
164
  }
165
165
  # pre-calculate zoomed mandelbrot images even before the user zooms in
166
166
  puts 'Starting background calculation thread...'
167
- Thread.new {
167
+ @thread = Thread.new {
168
168
  future_zoom = 1.5
169
169
  loop {
170
170
  puts "Creating mandelbrot for background calculation at zoom: #{future_zoom}"
@@ -180,16 +180,21 @@ class MandelbrotFractal
180
180
  body {
181
181
  shell(:no_resize) {
182
182
  grid_layout
183
- text bind(self, :mandelbrot_shell_title)
183
+ text <= [self, :mandelbrot_shell_title]
184
184
  minimum_size mandelbrot.width + 29, mandelbrot.height + 77
185
185
  image @mandelbrot_image
186
+
187
+ on_shell_closed {
188
+ @thread.kill # should not be dangerous in this case
189
+ puts "Mandelbrot background calculation stopped!"
190
+ }
186
191
 
187
192
  progress_bar {
188
193
  layout_data :fill, :center, true, false
189
194
 
190
195
  minimum 0
191
196
  maximum Mandelbrot::PROGRESS_MAX
192
- selection bind(Mandelbrot, :progress)
197
+ selection <= [Mandelbrot, :progress]
193
198
  }
194
199
 
195
200
  @scrolled_composite = scrolled_composite {