glimmer-dsl-swt 4.18.5.4 → 4.18.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,135 @@
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/custom/shape'
23
+
24
+ module Glimmer
25
+ module SWT
26
+ module Custom
27
+ class Shape
28
+ # Represents path segments like point, line, quad, and cubic curves
29
+ # Shapes could mix in
30
+ module PathSegment
31
+ def root_path
32
+ current_parent = parent
33
+ until current_parent.class == Path && !current_parent.parent.is_a?(Path)
34
+ current_parent = current_parent.parent
35
+ return current_parent if current_parent.nil?
36
+ end
37
+ current_parent
38
+ end
39
+ def path
40
+ current_parent = parent
41
+ until current_parent.class == Path
42
+ current_parent = current_parent.parent
43
+ return current_parent if current_parent.nil?
44
+ end
45
+ current_parent
46
+ end
47
+ # this is needed to indicate if a shape is part of a path or not (e.g. line and point could be either)
48
+ def part_of_path?
49
+ !!root_path
50
+ end
51
+ # Subclasses must override and implement to indicate method name to invoke on SWT Path object to add segment
52
+ def path_segment_method_name
53
+ nil
54
+ end
55
+ # Subclasses must override and implement to indicate args to pass when invoking SWT Path object method
56
+ def path_segment_args
57
+ []
58
+ end
59
+ # Subclasses must override to indicate expected complete count of args when previous point is NOT connected (e.g. 4 for line, 6 for quad, 8 for cubic)
60
+ def default_path_segment_arg_count
61
+ end
62
+ # Subclasses must override to indicate expected count of args when previous point IS connected (e.g. 2 for line, 4 for quad, 6 for cubic)
63
+ def default_connected_path_segment_arg_count
64
+ end
65
+ # Subclasses may override to provide name of method to invoke for geometry object obtained from the Java AWT library java.awt.geom.Path2D.Double (e.g. curveTo vs cubicTo)
66
+ def path_segment_geometry_method_name
67
+ path_segment_method_name
68
+ end
69
+ # Subclasses must override and implement to indicate args to pass when invoking SWT Path object method
70
+ def path_segment_geometry_args
71
+ path_segment_args
72
+ end
73
+ # Subclasses must override to indicate otherwise
74
+ def previous_point_connected?
75
+ true
76
+ end
77
+
78
+ def dispose(redraw: true)
79
+ Glimmer::SWT::DisplayProxy.instance.auto_exec do
80
+ # including classes could override to dispose of resources first
81
+ # afterwards, parent removes from its path segments with post_dispose_content
82
+ parent.post_dispose_content(self) if parent.is_a?(Path)
83
+ if part_of_path?
84
+ drawable.redraw if redraw && !drawable.is_a?(ImageProxy)
85
+ else
86
+ super(redraw: redraw)
87
+ end
88
+ end
89
+ end
90
+
91
+ def first_path_segment?
92
+ parent.path_segments.first == self
93
+ end
94
+
95
+ def previous_path_segment
96
+ parent.path_segments[parent.path_segments.index(self) - 1] || self
97
+ end
98
+
99
+ def add_to_swt_path(swt_path)
100
+ the_path_segment_args = path_segment_args.dup
101
+ if !is_a?(Point) && self.class != Path
102
+ if !previous_point_connected?
103
+ if the_path_segment_args.count == default_path_segment_arg_count
104
+ point = the_path_segment_args.shift, the_path_segment_args.shift
105
+ swt_path.moveTo(*point)
106
+ elsif first_path_segment?
107
+ point = the_path_segment_args[0..1]
108
+ swt_path.moveTo(*point)
109
+ end
110
+ end
111
+ end
112
+ swt_path.send(path_segment_method_name, *the_path_segment_args)
113
+ end
114
+
115
+ def add_to_geometry(geometry)
116
+ the_path_segment_geometry_args = path_segment_geometry_args.dup
117
+ if !is_a?(Point) && self.class != Path
118
+ if !previous_point_connected?
119
+ if the_path_segment_geometry_args.count == default_path_segment_arg_count
120
+ point = the_path_segment_geometry_args.shift, the_path_segment_geometry_args.shift
121
+ geometry.moveTo(point[0], point[1])
122
+ elsif first_path_segment?
123
+ point = the_path_segment_geometry_args[0..1]
124
+ geometry.moveTo(point[0], point[1])
125
+ end
126
+ end
127
+ end
128
+ geometry.send(path_segment_geometry_method_name, *the_path_segment_geometry_args)
129
+ end
130
+
131
+ end
132
+ end
133
+ end
134
+ end
135
+ 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/swt/custom/shape'
23
+ require 'glimmer/swt/custom/shape/path_segment'
23
24
  require 'glimmer/swt/swt_proxy'
24
25
  require 'glimmer/swt/display_proxy'
25
26
  require 'glimmer/swt/color_proxy'
@@ -33,6 +34,8 @@ module Glimmer
33
34
  # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
34
35
  class Shape
35
36
  class Point < Shape
37
+ include PathSegment
38
+
36
39
  def parameter_names
37
40
  [:x, :y]
38
41
  end
@@ -50,6 +53,36 @@ module Glimmer
50
53
  x.to_i.between?(self.absolute_x.to_i - 2, self.absolute_x.to_i + 2) && y.to_i.between?(self.absolute_y.to_i - 2, self.absolute_y.to_i + 2)
51
54
  end
52
55
  alias contain? include?
56
+
57
+ def path_segment_method_name
58
+ 'addRectangle'
59
+ end
60
+
61
+ def path_segment_args
62
+ @args + [1, 1]
63
+ end
64
+
65
+ def path_segment_geometry_method_name
66
+ 'moveTo'
67
+ end
68
+
69
+ def path_segment_geometry_args
70
+ @args
71
+ end
72
+
73
+ def previous_point_connected?
74
+ false
75
+ end
76
+
77
+ def eql?(other)
78
+ other.is_a?(Point) && x == (other && other.respond_to?(:x) && other.x) && y == (other && other.respond_to?(:y) && other.y)
79
+ end
80
+ alias == eql?
81
+
82
+ def hash
83
+ [x, y].hash
84
+ end
85
+
53
86
  end
54
87
  end
55
88
  end
@@ -132,21 +132,30 @@ module Glimmer
132
132
  end
133
133
 
134
134
  def width
135
- bounds.width
135
+ size.x
136
136
  end
137
137
 
138
138
  def height
139
- bounds.height
139
+ size.y
140
140
  end
141
141
 
142
- def include?(x, y)
142
+ def contain?(x, y)
143
143
  geometry.contains(x, y)
144
144
  end
145
- alias contain? include? # TODO make include do an outer/inner check of edge detection only
146
-
145
+
146
+ def include?(x, y)
147
+ comparison_lines = absolute_point_xy_array.zip(absolute_point_xy_array.rotate(1))
148
+ comparison_lines.any? {|line| Line.include?(line.first.first, line.first.last, line.last.first, line.last.last, x, y)}
149
+ end
150
+
147
151
  def move_by(x_delta, y_delta)
148
152
  self.point_array = point_array.each_with_index.map {|coordinate, i| i.even? ? coordinate + x_delta : coordinate + y_delta}
149
153
  end
154
+
155
+ def irregular?
156
+ true
157
+ end
158
+
150
159
  end
151
160
  end
152
161
  end
@@ -134,6 +134,11 @@ module Glimmer
134
134
  def move_by(x_delta, y_delta)
135
135
  self.point_array = point_array.each_with_index.map {|coordinate, i| i.even? ? coordinate + x_delta : coordinate + y_delta}
136
136
  end
137
+
138
+ def irregular?
139
+ true
140
+ end
141
+
137
142
  end
138
143
  end
139
144
  end
@@ -0,0 +1,114 @@
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/custom/shape'
23
+ require 'glimmer/swt/custom/shape/path'
24
+ require 'glimmer/swt/custom/shape/path_segment'
25
+ require 'glimmer/swt/swt_proxy'
26
+ require 'glimmer/swt/display_proxy'
27
+ require 'glimmer/swt/color_proxy'
28
+ require 'glimmer/swt/font_proxy'
29
+ require 'glimmer/swt/transform_proxy'
30
+
31
+ module Glimmer
32
+ module SWT
33
+ module Custom
34
+ # Represents a shape (graphics) to be drawn on a control/widget/canvas/display
35
+ # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
36
+ class Shape
37
+ class Quad < Path
38
+ def parameter_names
39
+ [:point_array]
40
+ end
41
+
42
+ def geometry
43
+ the_point_array = point_array
44
+ if the_point_array != @geometry_point_array
45
+ @geometry_point_array = the_point_array
46
+ @geometry = Java::JavaAwtGeom::Path2D::Double.new
47
+ add_to_geometry(@geometry)
48
+ end
49
+ @geometry
50
+ end
51
+
52
+ def contain?(x, y)
53
+ include?(x, y, filled: true)
54
+ end
55
+
56
+ # checks if drawn or filled rectangle includes the point denoted by x and y (if drawn, it only returns true if point lies on the edge)
57
+ def include?(x, y, filled: nil)
58
+ filled = filled? if filled.nil?
59
+ makeshift_gc = org.eclipse.swt.graphics.GC.new(Glimmer::SWT::DisplayProxy.instance.swt_display)
60
+ swt_path = org.eclipse.swt.graphics.Path.new(Glimmer::SWT::DisplayProxy.instance.swt_display)
61
+ the_path_segment_args = path_segment_args.dup
62
+ if previous_point_connected?
63
+ the_previous_path_segment = previous_path_segment
64
+ swt_path.moveTo(the_previous_path_segment.x, the_previous_path_segment.y)
65
+ else
66
+ swt_path.moveTo(the_path_segment_args.shift, the_path_segment_args.shift)
67
+ end
68
+ swt_path.quadTo(*the_path_segment_args)
69
+ swt_path.contains(x.to_f, y.to_f, makeshift_gc, !filled)
70
+ ensure
71
+ swt_path.dispose
72
+ end
73
+
74
+ def move_by(x_delta, y_delta)
75
+ the_point_array = @args.compact
76
+ the_point_array = the_point_array.first if the_point_array.first.is_a?(Array)
77
+ self.point_array = the_point_array.each_with_index.map {|coordinate, i| i.even? ? coordinate + x_delta : coordinate + y_delta}
78
+ end
79
+
80
+ def path_segment_method_name
81
+ 'quadTo'
82
+ end
83
+
84
+ def path_segment_args
85
+ # TODO make args auto-infer control points if previous_point_connected is true or if there is only a point_array with 1 point
86
+ @args
87
+ end
88
+
89
+ def default_path_segment_arg_count
90
+ 6
91
+ end
92
+
93
+ def default_connected_path_segment_arg_count
94
+ 4
95
+ end
96
+
97
+ def previous_point_connected?
98
+ @args.compact.count == 4 && !first_path_segment?
99
+ end
100
+
101
+ def eql?(other)
102
+ other.is_a?(Quad) && point_array == (other && other.respond_to?(:point_array) && other.point_array)
103
+ end
104
+ alias == eql?
105
+
106
+ def hash
107
+ point_array.hash
108
+ end
109
+
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -52,6 +52,10 @@ module Glimmer
52
52
  def dialog_class(keyword)
53
53
  the_class = eval(keyword.camelcase(:upper))
54
54
  the_class if the_class.ancestors.include?(org.eclipse.swt.widgets.Dialog)
55
+ rescue => e
56
+ Glimmer::Config.logger.debug {"Dialog for keyword #{keyword} not found!"}
57
+ Glimmer::Config.logger.debug { e.full_message }
58
+ nil
55
59
  end
56
60
  end
57
61
 
@@ -73,7 +73,7 @@ module Glimmer
73
73
  Glimmer::SWT::DisplayProxy.instance.auto_exec do
74
74
  result = if proxy_source_object&.respond_to?(attribute_setter(attribute_name))
75
75
  swt_widget_operation = true
76
- proxy_source_object&.send(attribute_setter(attribute_name), *args) unless proxy_source_object&.send(attribute_getter(attribute_name)) == args.first
76
+ proxy_source_object&.send(attribute_setter(attribute_name), *args) unless (proxy_source_object&.respond_to?(attribute_getter(attribute_name)) && proxy_source_object&.send(attribute_getter(attribute_name))) == args.first
77
77
  elsif proxy_source_object&.respond_to?(ruby_attribute_setter(attribute_name))
78
78
  swt_widget_operation = true
79
79
  proxy_source_object&.send(ruby_attribute_setter(attribute_name), args)
@@ -0,0 +1,52 @@
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
+
24
+ module Glimmer
25
+ module SWT
26
+ # Proxy for org.eclipse.swt.widgets.TabProxy
27
+ #
28
+ # Follows the Proxy Design Pattern
29
+ class TabFolderProxy < WidgetProxy
30
+ def initialize(underscored_widget_name, parent, args, swt_widget: nil)
31
+ @initialize_tabs_on_select = args.delete(:initialize_tabs_on_select)
32
+ super
33
+ end
34
+
35
+ def post_add_content
36
+ shown = false
37
+ unless @initialize_tabs_on_select
38
+ @show_listener = parent_proxy.on_swt_show do
39
+ unless shown
40
+ shown = true
41
+ self.items.to_a.each do |item|
42
+ self.selection = item
43
+ end
44
+ self.selection = self.items.first unless self.items.first.nil?
45
+ @show_listener.deregister
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -379,7 +379,7 @@ class MandelbrotFractal
379
379
  image @mandelbrot_image
380
380
  }
381
381
  @canvas.set_size @mandelbrot_image.bounds.width, @mandelbrot_image.bounds.height
382
- @scrolled_composite.swt_widget.set_min_size(Point.new(@mandelbrot_image.bounds.width, @mandelbrot_image.bounds.height))
382
+ @scrolled_composite.set_min_size(Point.new(@mandelbrot_image.bounds.width, @mandelbrot_image.bounds.height))
383
383
  if @location_x && @location_y
384
384
  # center on mouse click location
385
385
  factor = (zoom / last_zoom)
@@ -0,0 +1,214 @@
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-dsl-swt'
23
+
24
+ # This Sample is an Early Alpha (New Canvas Path DSL Feature)
25
+
26
+ class StockTicker
27
+ class Stock
28
+ class << self
29
+ attr_writer :price_min, :price_max
30
+
31
+ def price_min
32
+ @price_min ||= 1
33
+ end
34
+
35
+ def price_max
36
+ @price_max ||= 600
37
+ end
38
+ end
39
+
40
+ attr_reader :name, :prices
41
+ attr_accessor :price
42
+
43
+ def initialize(name, price)
44
+ @name = name
45
+ @price = price
46
+ @prices = [@price]
47
+ @delta_sign = 1
48
+ start_new_trend!
49
+ end
50
+
51
+ def tick!
52
+ @tick_count = @tick_count.to_i + 1
53
+ delta = @tick_count%@trend_length
54
+ if delta == 0
55
+ @delta_sign *= -1
56
+ start_new_trend!
57
+ end
58
+ prices << self.price = [[@price + @delta_sign*delta, Stock.price_min].max, Stock.price_max].min
59
+ end
60
+
61
+ def start_new_trend!
62
+ @trend_length = (rand*12).to_i + 1
63
+ end
64
+ end
65
+
66
+ include Glimmer::UI::CustomShell
67
+
68
+ before_body {
69
+ @stocks = [
70
+ Stock.new('DELL', 81),
71
+ Stock.new('AAPL', 121),
72
+ Stock.new('MSFT', 232),
73
+ Stock.new('ADBE', 459),
74
+ ]
75
+ @stock_colors = [:red, :dark_green, :blue, :dark_magenta]
76
+ margin = 5
77
+ @tabs = ['Lines', 'Quadratic Bezier Curves', 'Cubic Bezier Curves', 'Points'].map {|title| {title: title, stock_paths: []}}
78
+ @stocks.each_with_index do |stock, stock_index|
79
+ observe(stock, :price) do |new_price|
80
+ begin
81
+ @tabs.each do |tab|
82
+ new_x = stock.prices.count - 1
83
+ new_y = @tabs.first[:canvas].bounds.height - new_price - 1
84
+ if new_x > 0
85
+ case tab[:title]
86
+ when 'Cubic Bezier Curves'
87
+ if new_x%3 == 0 && stock.prices[new_x] && stock.prices[new_x - 1] && stock.prices[new_x - 2]
88
+ tab[:stock_paths][stock_index].content {
89
+ cubic(new_x - 2 + margin, @tabs.first[:canvas].bounds.height - stock.prices[new_x - 2] - 1, new_x - 1 + margin, @tabs.first[:canvas].bounds.height - stock.prices[new_x - 1] - 1, new_x + margin, new_y)
90
+ }
91
+ end
92
+ when 'Quadratic Bezier Curves'
93
+ if new_x%2 == 0 && stock.prices[new_x] && stock.prices[new_x - 1]
94
+ tab[:stock_paths][stock_index].content {
95
+ quad(new_x - 1 + margin, @tabs.first[:canvas].bounds.height - stock.prices[new_x - 1] - 1, new_x + margin, new_y)
96
+ }
97
+ end
98
+ when 'Lines'
99
+ tab[:stock_paths][stock_index].content {
100
+ line(new_x + margin, new_y)
101
+ }
102
+ when 'Points'
103
+ tab[:stock_paths][stock_index].content {
104
+ point(new_x + margin, new_y)
105
+ }
106
+ end
107
+ new_x_location = new_x + 2*margin
108
+ canvas_width = tab[:canvas].bounds.width
109
+ if new_x_location > canvas_width
110
+ tab[:canvas].set_size(new_x_location, @tabs.first[:canvas].bounds.height)
111
+ tab[:canvas].cursor = :hand
112
+ tab[:scrolled_composite].set_min_size(new_x_location, @tabs.first[:canvas].bounds.height)
113
+ tab[:scrolled_composite].set_origin(tab[:scrolled_composite].origin.x + 1, tab[:scrolled_composite].origin.y) if (tab[:scrolled_composite].origin.x + tab[:scrolled_composite].client_area.width) == canvas_width
114
+ end
115
+ else
116
+ tab[:canvas_header].content {
117
+ text(stock.name, 15, new_y - 10) {
118
+ foreground @stock_colors[stock_index]
119
+ font height: 14
120
+ }
121
+ }
122
+ end
123
+ end
124
+ rescue => e
125
+ Glimmer::Config.logger.error {e.full_message}
126
+ end
127
+ end
128
+ end
129
+ }
130
+
131
+ after_body {
132
+ @thread = Thread.new {
133
+ loop {
134
+ @stocks.each(&:tick!)
135
+ sleep(0.01)
136
+ }
137
+ }
138
+ }
139
+
140
+ body {
141
+ shell {
142
+ fill_layout {
143
+ margin_width 15
144
+ margin_height 15
145
+ }
146
+ text 'Stock Ticker'
147
+ minimum_size 650, 650
148
+ background :white
149
+ @tab_folder = tab_folder {
150
+ @tabs.each do |tab|
151
+ tab_item {
152
+ grid_layout(2, false) {
153
+ margin_width 0
154
+ margin_height 0
155
+ }
156
+ text tab[:title]
157
+
158
+ tab[:canvas_header] = canvas {
159
+ layout_data(:center, :fill, false, true)
160
+ }
161
+ tab[:scrolled_composite] = scrolled_composite {
162
+ layout_data :fill, :fill, true, true
163
+ tab[:canvas] = canvas {
164
+ background :white
165
+
166
+ @stocks.count.times do |stock_index|
167
+ tab[:stock_paths][stock_index] = path {
168
+ antialias :on
169
+ foreground @stock_colors[stock_index]
170
+ }
171
+ end
172
+
173
+ on_mouse_down {
174
+ @drag_detected = false
175
+ }
176
+
177
+ on_drag_detected { |drag_detect_event|
178
+ @drag_detected = true
179
+ @drag_start_x = drag_detect_event.x
180
+ @drag_start_y = drag_detect_event.y
181
+ }
182
+
183
+ on_mouse_move { |mouse_event|
184
+ if @drag_detected
185
+ origin = tab[:scrolled_composite].origin
186
+ new_x = origin.x - (mouse_event.x - @drag_start_x)
187
+ new_y = origin.y - (mouse_event.y - @drag_start_y)
188
+ tab[:scrolled_composite].set_origin(new_x, new_y)
189
+ end
190
+ }
191
+
192
+ on_mouse_up { |mouse_event|
193
+ @drag_detected = false
194
+ }
195
+ }
196
+ }
197
+ }
198
+ end
199
+ }
200
+
201
+ on_swt_show {
202
+ Stock.price_min = 25
203
+ Stock.price_max = @tabs.first[:canvas].bounds.height - 6
204
+ }
205
+
206
+ on_widget_disposed {
207
+ @thread.kill # safe to kill as data is in memory only
208
+ }
209
+ }
210
+ }
211
+ end
212
+
213
+ StockTicker.launch
214
+