glimmer-dsl-swt 4.18.4.9 → 4.18.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -0
  3. data/README.md +14 -5
  4. data/VERSION +1 -1
  5. data/bin/glimmer +3 -3
  6. data/docs/reference/GLIMMER_CONFIGURATION.md +7 -3
  7. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +464 -149
  8. data/docs/reference/GLIMMER_SAMPLES.md +91 -4
  9. data/glimmer-dsl-swt.gemspec +24 -13
  10. data/lib/ext/glimmer/config.rb +3 -7
  11. data/lib/glimmer/data_binding/list_selection_binding.rb +13 -7
  12. data/lib/glimmer/data_binding/table_items_binding.rb +22 -17
  13. data/lib/glimmer/data_binding/tree_items_binding.rb +19 -15
  14. data/lib/glimmer/data_binding/widget_binding.rb +13 -15
  15. data/lib/glimmer/dsl/swt/{file_dialog_expression.rb → auto_exec_expression.rb} +6 -18
  16. data/lib/glimmer/dsl/swt/checkbox_group_selection_data_binding_expression.rb +9 -6
  17. data/lib/glimmer/dsl/swt/color_expression.rb +1 -1
  18. data/lib/glimmer/dsl/swt/combo_selection_data_binding_expression.rb +16 -14
  19. data/lib/glimmer/dsl/swt/custom_widget_expression.rb +4 -1
  20. data/lib/glimmer/dsl/swt/data_binding_expression.rb +2 -2
  21. data/lib/glimmer/dsl/swt/dialog_expression.rb +18 -9
  22. data/lib/glimmer/dsl/swt/dsl.rb +1 -0
  23. data/lib/glimmer/dsl/swt/exec_expression.rb +1 -1
  24. data/lib/glimmer/dsl/swt/font_expression.rb +1 -1
  25. data/lib/glimmer/dsl/swt/image_expression.rb +16 -2
  26. data/lib/glimmer/dsl/swt/list_selection_data_binding_expression.rb +11 -8
  27. data/lib/glimmer/dsl/swt/pixel_expression.rb +1 -1
  28. data/lib/glimmer/dsl/swt/radio_group_selection_data_binding_expression.rb +8 -5
  29. data/lib/glimmer/dsl/swt/shape_expression.rb +2 -2
  30. data/lib/glimmer/dsl/swt/shell_expression.rb +5 -2
  31. data/lib/glimmer/dsl/swt/widget_expression.rb +8 -4
  32. data/lib/glimmer/launcher.rb +3 -0
  33. data/lib/glimmer/rake_task/scaffold.rb +3 -0
  34. data/lib/glimmer/swt/color_proxy.rb +1 -1
  35. data/lib/glimmer/swt/custom/code_text.rb +33 -11
  36. data/lib/glimmer/swt/custom/drawable.rb +55 -0
  37. data/lib/glimmer/swt/custom/shape.rb +187 -43
  38. data/lib/glimmer/swt/custom/shape/arc.rb +60 -0
  39. data/lib/glimmer/{dsl/swt/directory_dialog_expression.rb → swt/custom/shape/focus.rb} +15 -20
  40. data/lib/glimmer/swt/custom/shape/image.rb +99 -0
  41. data/lib/glimmer/swt/custom/shape/line.rb +65 -0
  42. data/lib/glimmer/swt/custom/shape/oval.rb +61 -0
  43. data/lib/glimmer/swt/custom/shape/point.rb +54 -0
  44. data/lib/glimmer/swt/custom/shape/polygon.rb +73 -0
  45. data/lib/glimmer/swt/custom/shape/polyline.rb +74 -0
  46. data/lib/glimmer/swt/custom/shape/rectangle.rb +101 -0
  47. data/lib/glimmer/swt/custom/shape/text.rb +85 -0
  48. data/lib/glimmer/swt/date_time_proxy.rb +9 -3
  49. data/lib/glimmer/swt/dialog_proxy.rb +92 -0
  50. data/lib/glimmer/swt/display_proxy.rb +62 -2
  51. data/lib/glimmer/swt/expand_item_proxy.rb +18 -12
  52. data/lib/glimmer/swt/font_proxy.rb +13 -7
  53. data/lib/glimmer/swt/image_proxy.rb +15 -4
  54. data/lib/glimmer/swt/layout_data_proxy.rb +21 -15
  55. data/lib/glimmer/swt/layout_proxy.rb +19 -15
  56. data/lib/glimmer/swt/menu_proxy.rb +2 -2
  57. data/lib/glimmer/swt/message_box_proxy.rb +21 -7
  58. data/lib/glimmer/swt/properties.rb +3 -0
  59. data/lib/glimmer/swt/proxy_properties.rb +145 -0
  60. data/lib/glimmer/swt/scrolled_composite_proxy.rb +6 -2
  61. data/lib/glimmer/swt/shell_proxy.rb +96 -80
  62. data/lib/glimmer/swt/swt_proxy.rb +17 -0
  63. data/lib/glimmer/swt/tab_item_proxy.rb +5 -3
  64. data/lib/glimmer/swt/table_proxy.rb +32 -11
  65. data/lib/glimmer/swt/transform_proxy.rb +39 -35
  66. data/lib/glimmer/swt/tree_proxy.rb +11 -16
  67. data/lib/glimmer/swt/widget_listener_proxy.rb +6 -2
  68. data/lib/glimmer/swt/widget_proxy.rb +192 -141
  69. data/lib/glimmer/ui.rb +5 -0
  70. data/lib/glimmer/ui/custom_shell.rb +13 -7
  71. data/lib/glimmer/ui/custom_widget.rb +4 -5
  72. data/samples/elaborate/contact_manager.rb +7 -7
  73. data/samples/elaborate/login.rb +25 -21
  74. data/samples/elaborate/mandelbrot_fractal.rb +87 -31
  75. data/samples/elaborate/meta_sample.rb +1 -1
  76. data/samples/elaborate/tetris.rb +1 -0
  77. data/samples/elaborate/tic_tac_toe.rb +16 -14
  78. data/samples/elaborate/tic_tac_toe/board.rb +5 -5
  79. data/samples/elaborate/tic_tac_toe/cell.rb +5 -5
  80. data/samples/hello/hello_button.rb +7 -7
  81. data/samples/hello/hello_canvas.rb +143 -41
  82. data/samples/hello/hello_checkbox.rb +16 -14
  83. data/samples/hello/hello_checkbox_group.rb +11 -9
  84. data/samples/hello/hello_color_dialog.rb +66 -0
  85. data/samples/hello/hello_combo.rb +14 -12
  86. data/samples/hello/hello_computed.rb +7 -7
  87. data/samples/hello/hello_cursor.rb +2 -1
  88. data/samples/hello/hello_custom_shell.rb +17 -21
  89. data/samples/hello/hello_custom_widget.rb +4 -6
  90. data/samples/hello/hello_date_time.rb +14 -12
  91. data/samples/hello/hello_directory_dialog.rb +7 -7
  92. data/samples/hello/hello_expand_bar.rb +8 -8
  93. data/samples/hello/hello_file_dialog.rb +7 -7
  94. data/samples/hello/hello_font_dialog.rb +82 -0
  95. data/samples/hello/hello_group.rb +18 -16
  96. data/samples/hello/hello_list_multi_selection.rb +13 -11
  97. data/samples/hello/hello_list_single_selection.rb +13 -11
  98. data/samples/hello/hello_progress_bar.rb +125 -0
  99. data/samples/hello/hello_radio.rb +18 -16
  100. data/samples/hello/hello_radio_group.rb +14 -12
  101. data/samples/hello/hello_spinner.rb +7 -7
  102. data/samples/hello/hello_tab.rb +5 -5
  103. data/samples/hello/hello_table.rb +10 -5
  104. data/samples/hello/hello_tree.rb +485 -0
  105. metadata +22 -22
  106. data/lib/glimmer/swt/directory_dialog_proxy.rb +0 -65
  107. data/lib/glimmer/swt/file_dialog_proxy.rb +0 -66
data/lib/glimmer/ui.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Glimmer
2
+ module UI
3
+ end
4
+ GUI = UI # alias
5
+ end
@@ -19,6 +19,7 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
+ require 'glimmer/ui'
22
23
  require 'glimmer/error'
23
24
 
24
25
  module Glimmer
@@ -28,19 +29,22 @@ module Glimmer
28
29
  include Glimmer::UI::CustomWidget
29
30
 
30
31
  class << self
31
- attr_reader :launched_custom_shell
32
-
33
32
  def launch(*args, &content)
34
- @launched_custom_shell = send(keyword, *args, &content) if @launched_custom_shell.nil? || @launched_custom_shell.disposed?
35
- @launched_custom_shell.swt_widget.set_data('launched', true)
36
- @launched_custom_shell.open
33
+ auto_exec do
34
+ @launched_custom_shell = send(keyword, *args, &content)
35
+ @launched_custom_shell.swt_widget.set_data('launched', true)
36
+ @launched_custom_shell.open
37
+ end
37
38
  end
38
39
  end
39
40
 
40
41
  def initialize(parent, *swt_constants, options, &content)
41
42
  super
42
- @swt_widget.set_data('custom_shell', self)
43
- raise Error, 'Invalid custom shell body root! Must be a shell or another custom shell.' unless body_root.swt_widget.is_a?(org.eclipse.swt.widgets.Shell)
43
+ auto_exec do
44
+ @swt_widget.set_data('custom_shell', self)
45
+ @swt_widget.set_data('custom_window', self)
46
+ end
47
+ raise Error, 'Invalid custom shell (window) body root! Must be a shell (window) or another custom shell (window).' unless body_root.swt_widget.is_a?(org.eclipse.swt.widgets.Shell)
44
48
  end
45
49
 
46
50
  # Classes may override
@@ -78,5 +82,7 @@ module Glimmer
78
82
  body_root.start_event_loop
79
83
  end
80
84
  end
85
+ CustomWindow = CustomShell
86
+ Application = CustomShell
81
87
  end
82
88
  end
@@ -20,6 +20,7 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'glimmer'
23
+ require 'glimmer/ui'
23
24
  require 'glimmer/error'
24
25
  require 'glimmer/swt/swt_proxy'
25
26
  require 'glimmer/swt/display_proxy'
@@ -181,7 +182,9 @@ module Glimmer
181
182
  @body_root = instance_exec(&body_block)
182
183
  raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
183
184
  @swt_widget = @body_root.swt_widget
184
- @swt_widget.set_data('custom_widget', self)
185
+ auto_exec do
186
+ @swt_widget.set_data('custom_widget', self)
187
+ end
185
188
  execute_hook('after_body')
186
189
  @dispose_listener_registration = @body_root.on_widget_disposed do
187
190
  observer_registrations.each(&:deregister)
@@ -271,10 +274,6 @@ module Glimmer
271
274
  (swt_style & SWT::SWTProxy[style]) == SWT::SWTProxy[style]
272
275
  end
273
276
 
274
- def pack(*args)
275
- body_root.pack(*args)
276
- end
277
-
278
277
  # TODO see if it is worth it to eliminate duplication of async_exec/sync_exec
279
278
  # delegation to DisplayProxy, via a module
280
279
 
@@ -22,14 +22,14 @@
22
22
  require_relative "contact_manager/contact_manager_presenter"
23
23
 
24
24
  class ContactManager
25
- include Glimmer
25
+ include Glimmer::UI::CustomShell
26
26
 
27
- def initialize
27
+ before_body {
28
28
  @contact_manager_presenter = ContactManagerPresenter.new
29
29
  @contact_manager_presenter.list
30
- end
30
+ }
31
31
 
32
- def launch
32
+ body {
33
33
  shell {
34
34
  text "Contact Manager"
35
35
  composite {
@@ -135,8 +135,8 @@ class ContactManager
135
135
  }
136
136
  }
137
137
  }
138
- }.open
139
- end
138
+ }
139
+ }
140
140
  end
141
141
 
142
- ContactManager.new.launch
142
+ ContactManager.launch
@@ -45,17 +45,19 @@ class LoginPresenter
45
45
  def logged_in
46
46
  self.status == "Logged In"
47
47
  end
48
+ alias logged_in? logged_in
48
49
 
49
50
  def logged_out
50
51
  !self.logged_in
51
52
  end
53
+ alias logged_out? logged_out
52
54
 
53
- def login
55
+ def login!
54
56
  return unless valid?
55
57
  self.status = "Logged In"
56
58
  end
57
59
 
58
- def logout
60
+ def logout!
59
61
  self.user_name = ""
60
62
  self.password = ""
61
63
  self.status = "Logged Out"
@@ -64,19 +66,22 @@ class LoginPresenter
64
66
  end
65
67
 
66
68
  class Login
67
- include Glimmer
69
+ include Glimmer::UI::CustomShell
68
70
 
69
- def launch
70
- presenter = LoginPresenter.new
71
- @shell = shell {
71
+ before_body {
72
+ @presenter = LoginPresenter.new
73
+ }
74
+
75
+ body {
76
+ shell {
72
77
  text "Login"
73
78
  composite {
74
79
  grid_layout 2, false #two columns with differing widths
75
80
 
76
81
  label { text "Username:" } # goes in column 1
77
82
  @user_name_text = text { # goes in column 2
78
- text bind(presenter, :user_name)
79
- enabled bind(presenter, :logged_out)
83
+ text bind(@presenter, :user_name)
84
+ enabled bind(@presenter, :logged_out)
80
85
  on_key_pressed { |event|
81
86
  @password_text.set_focus if event.keyCode == swt(:cr)
82
87
  }
@@ -84,40 +89,39 @@ class Login
84
89
 
85
90
  label { text "Password:" }
86
91
  @password_text = text(:password, :border) {
87
- text bind(presenter, :password)
88
- enabled bind(presenter, :logged_out)
92
+ text bind(@presenter, :password)
93
+ enabled bind(@presenter, :logged_out)
89
94
  on_key_pressed { |event|
90
- presenter.login if event.keyCode == swt(:cr)
95
+ @presenter.login! if event.keyCode == swt(:cr)
91
96
  }
92
97
  }
93
98
 
94
99
  label { text "Status:" }
95
- label { text bind(presenter, :status) }
100
+ label { text bind(@presenter, :status) }
96
101
 
97
102
  button {
98
103
  text "Login"
99
- enabled bind(presenter, :logged_out)
100
- on_widget_selected { presenter.login }
104
+ enabled bind(@presenter, :logged_out)
105
+ on_widget_selected { @presenter.login! }
101
106
  on_key_pressed { |event|
102
- presenter.login if event.keyCode == swt(:cr)
107
+ @presenter.login! if event.keyCode == swt(:cr)
103
108
  }
104
109
  }
105
110
 
106
111
  button {
107
112
  text "Logout"
108
- enabled bind(presenter, :logged_in)
109
- on_widget_selected { presenter.logout }
113
+ enabled bind(@presenter, :logged_in)
114
+ on_widget_selected { @presenter.logout! }
110
115
  on_key_pressed { |event|
111
116
  if event.keyCode == swt(:cr)
112
- presenter.logout
117
+ @presenter.logout!
113
118
  @user_name_text.set_focus
114
119
  end
115
120
  }
116
121
  }
117
122
  }
118
123
  }
119
- @shell.open
120
- end
124
+ }
121
125
  end
122
126
 
123
- Login.new.launch
127
+ Login.launch
@@ -20,7 +20,6 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'complex'
23
- require 'bigdecimal'
24
23
  require 'concurrent-ruby'
25
24
 
26
25
  # Mandelbrot multi-threaded implementation leveraging all processor cores.
@@ -30,8 +29,12 @@ class Mandelbrot
30
29
  Y_END = 1.0
31
30
  X_START = -2.0
32
31
  X_END = 0.5
32
+ PROGRESS_MAX = 40
33
33
 
34
34
  class << self
35
+ attr_accessor :progress, :work_in_progress
36
+ attr_writer :processor_count
37
+
35
38
  def for(max_iterations:, zoom:, background: false)
36
39
  key = [max_iterations, zoom]
37
40
  creation_mutex.synchronize do
@@ -50,6 +53,10 @@ class Mandelbrot
50
53
  def creation_mutex
51
54
  @creation_mutex ||= Mutex.new
52
55
  end
56
+
57
+ def processor_count
58
+ @processor_count ||= Concurrent.physical_processor_count
59
+ end
53
60
  end
54
61
 
55
62
  attr_accessor :max_iterations, :background
@@ -63,7 +70,6 @@ class Mandelbrot
63
70
  def initialize(max_iterations:, zoom: 1.0, background: false)
64
71
  @max_iterations = max_iterations
65
72
  @zoom = zoom
66
- @background = background
67
73
  end
68
74
 
69
75
  def step
@@ -90,28 +96,31 @@ class Mandelbrot
90
96
  @points ||= calculate_points
91
97
  end
92
98
 
93
- def thread_count
94
- @background ? [Concurrent.processor_count - 1, 1].max : Concurrent.processor_count
95
- end
96
-
97
99
  def calculate_points
98
100
  puts "Background calculation activated at zoom #{zoom}" if @background
99
101
  if @points_calculated
100
102
  puts "Points calculated already. Returning previously calculated points..."
101
103
  return @points
102
104
  end
103
- thread_pool = Concurrent::FixedThreadPool.new(thread_count, fallback_policy: :discard)
105
+ thread_pool = Concurrent::FixedThreadPool.new(Mandelbrot.processor_count, fallback_policy: :discard)
104
106
  @points = Concurrent::Array.new(height)
107
+ Mandelbrot.work_in_progress = "Calculating Mandelbrot Points for Zoom #{zoom}x"
108
+ Mandelbrot.progress = 0
109
+ point_index = 0
110
+ point_count = width*height
105
111
  height.times do |y|
106
112
  @points[y] ||= Concurrent::Array.new(width)
107
113
  width.times do |x|
108
114
  thread_pool.post do
109
115
  @points[y][x] = calculate(x_array[x], y_array[y]).last
116
+ point_index += 1
117
+ Mandelbrot.progress += 1 if (point_index.to_f / point_count.to_f)*PROGRESS_MAX >= Mandelbrot.progress
110
118
  end
111
119
  end
112
120
  end
113
121
  thread_pool.shutdown
114
122
  thread_pool.wait_for_termination
123
+ Mandelbrot.progress = PROGRESS_MAX
115
124
  @points_calculated = true
116
125
  @points
117
126
  end
@@ -132,9 +141,11 @@ end
132
141
 
133
142
  class MandelbrotFractal
134
143
  include Glimmer::UI::CustomShell
135
-
136
- COMMAND = OS.mac? ? :command : :ctrl
137
144
 
145
+ COMMAND = OS.mac? ? :command : :ctrl
146
+
147
+ attr_accessor :mandelbrot_shell_title
148
+
138
149
  option :zoom, default: 1.0
139
150
 
140
151
  before_body {
@@ -144,6 +155,12 @@ class MandelbrotFractal
144
155
  }
145
156
 
146
157
  after_body {
158
+ observe(Mandelbrot, :work_in_progress) {
159
+ update_mandelbrot_shell_title!
160
+ }
161
+ observe(Mandelbrot, :zoom) {
162
+ update_mandelbrot_shell_title!
163
+ }
147
164
  # pre-calculate zoomed mandelbrot images even before the user zooms in
148
165
  puts 'Starting background calculation thread...'
149
166
  Thread.new {
@@ -153,10 +170,7 @@ class MandelbrotFractal
153
170
  the_mandelbrot = Mandelbrot.for(max_iterations: color_palette.size - 1, zoom: future_zoom, background: true)
154
171
  pixels = the_mandelbrot.calculate_points
155
172
  build_mandelbrot_image(mandelbrot_zoom: future_zoom)
156
- sync_exec {
157
- swt_widget.text = mandelbrot_shell_title
158
- @canvas.cursor = :cross
159
- }
173
+ @canvas.cursor = :cross unless @canvas.disposed?
160
174
  future_zoom += 0.5
161
175
  }
162
176
  }
@@ -164,11 +178,21 @@ class MandelbrotFractal
164
178
 
165
179
  body {
166
180
  shell(:no_resize) {
167
- text mandelbrot_shell_title
168
- minimum_size mandelbrot.width, mandelbrot.height + 30
181
+ grid_layout
182
+ text bind(self, :mandelbrot_shell_title)
183
+ minimum_size mandelbrot.width + 29, mandelbrot.height + 77
169
184
  image @mandelbrot_image
170
185
 
186
+ progress_bar {
187
+ layout_data :fill, :center, true, false
188
+
189
+ minimum 0
190
+ maximum Mandelbrot::PROGRESS_MAX
191
+ selection bind(Mandelbrot, :progress)
192
+ }
193
+
171
194
  @scrolled_composite = scrolled_composite {
195
+ layout_data :fill, :fill, true, true
172
196
  @canvas = canvas {
173
197
  image @mandelbrot_image
174
198
  cursor :no
@@ -196,8 +220,8 @@ class MandelbrotFractal
196
220
  on_mouse_up { |mouse_event|
197
221
  if !@drag_detected
198
222
  origin = @scrolled_composite.origin
199
- @location_x = [[origin.x + mouse_event.x - @scrolled_composite.bounds.width / 2.0, 0].max, @scrolled_composite.bounds.width].min
200
- @location_y = [[origin.y + mouse_event.y - @scrolled_composite.bounds.height / 2.0, 0].max, @scrolled_composite.bounds.height].min
223
+ @location_x = mouse_event.x
224
+ @location_y = mouse_event.y
201
225
  if mouse_event.button == 1
202
226
  zoom_in
203
227
  elsif mouse_event.button > 2
@@ -236,6 +260,31 @@ class MandelbrotFractal
236
260
  on_widget_selected { perform_zoom(mandelbrot_zoom: 1.0) }
237
261
  }
238
262
  }
263
+ menu {
264
+ text '&Cores'
265
+
266
+ Concurrent.physical_processor_count.times {|n|
267
+ processor_number = n + 1
268
+ menu_item(:radio) {
269
+ text "&#{processor_number}"
270
+
271
+ case processor_number
272
+ when 0..9
273
+ accelerator COMMAND, processor_number.to_s
274
+ when 10..19
275
+ accelerator COMMAND, :shift, (processor_number - 10).to_s
276
+ when 20..29
277
+ accelerator COMMAND, :alt, (processor_number - 20).to_s
278
+ end
279
+
280
+ selection true if processor_number == Concurrent.physical_processor_count
281
+
282
+ on_widget_selected {
283
+ Mandelbrot.processor_count = processor_number
284
+ }
285
+ }
286
+ }
287
+ }
239
288
  menu {
240
289
  text '&Help'
241
290
 
@@ -251,9 +300,11 @@ class MandelbrotFractal
251
300
  }
252
301
  }
253
302
  }
254
-
255
- def mandelbrot_shell_title
256
- "Mandelbrot Fractal - Zoom #{zoom}x (Calculated Max: #{flyweight_mandelbrot_images.keys.max}x)"
303
+
304
+ def update_mandelbrot_shell_title!
305
+ new_title = "Mandelbrot Fractal - Zoom #{zoom}x (Calculated Max: #{flyweight_mandelbrot_images.keys.max}x)"
306
+ new_title += " - #{Mandelbrot.work_in_progress}" if Mandelbrot.work_in_progress
307
+ self.mandelbrot_shell_title = new_title
257
308
  end
258
309
 
259
310
  def build_mandelbrot_image(mandelbrot_zoom: nil)
@@ -263,17 +314,20 @@ class MandelbrotFractal
263
314
  width = the_mandelbrot.width
264
315
  height = the_mandelbrot.height
265
316
  pixels = the_mandelbrot.points
266
- new_mandelbrot_image = image(width, height, top_level: true) # invoke as a top-level parentless keyword to avoid nesting under any widget
267
- new_mandelbrot_image_gc = new_mandelbrot_image.gc
268
- current_foreground = nil
269
- height.times { |y|
270
- width.times { |x|
271
- new_foreground = color_palette[pixels[y][x]]
272
- new_mandelbrot_image_gc.foreground = current_foreground = new_foreground unless new_foreground == current_foreground
273
- new_mandelbrot_image_gc.draw_point x, y
274
- }
317
+ Mandelbrot.work_in_progress = "Consuming Points To Build Image for Zoom #{mandelbrot_zoom}x"
318
+ Mandelbrot.progress = Mandelbrot::PROGRESS_MAX + 1
319
+ point_index = 0
320
+ point_count = width*height
321
+ # invoke as a top-level parentless keyword to avoid nesting under any widget
322
+ new_mandelbrot_image = image(width, height, top_level: true) { |x, y|
323
+ point_index += 1
324
+ Mandelbrot.progress -= 1 if (Mandelbrot::PROGRESS_MAX - (point_index.to_f / point_count.to_f)*Mandelbrot::PROGRESS_MAX) < Mandelbrot.progress
325
+ pixel_color_index = pixels[y][x]
326
+ color_palette[pixel_color_index]
275
327
  }
328
+ Mandelbrot.progress = 0
276
329
  flyweight_mandelbrot_images[mandelbrot_zoom] = new_mandelbrot_image
330
+ update_mandelbrot_shell_title!
277
331
  end
278
332
  flyweight_mandelbrot_images[mandelbrot_zoom]
279
333
  end
@@ -326,12 +380,13 @@ class MandelbrotFractal
326
380
  @canvas.set_size @mandelbrot_image.bounds.width, @mandelbrot_image.bounds.height
327
381
  @scrolled_composite.swt_widget.set_min_size(Point.new(@mandelbrot_image.bounds.width, @mandelbrot_image.bounds.height))
328
382
  if @location_x && @location_y
383
+ # center on mouse click location
329
384
  factor = (zoom / last_zoom)
330
- @scrolled_composite.set_origin(factor*@location_x, factor*@location_y)
385
+ @scrolled_composite.set_origin(factor*@location_x - @scrolled_composite.client_area.width/2.0, factor*@location_y - @scrolled_composite.client_area.height/2.0)
331
386
  @location_x = @location_y = nil
332
387
  end
388
+ update_mandelbrot_shell_title!
333
389
  @canvas.cursor = :cross
334
- swt_widget.text = mandelbrot_shell_title
335
390
  end
336
391
 
337
392
  def display_help_instructions
@@ -343,6 +398,7 @@ class MandelbrotFractal
343
398
  Left-click to zoom in.
344
399
  Right-click to zoom out.
345
400
  Scroll or drag to pan.
401
+ Adjust cores to get a more responsive interaction.
346
402
 
347
403
  Enjoy!
348
404
  MULTI_LINE_STRING