glimmer-dsl-swt 4.18.0.2 → 4.18.2.2

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,243 @@
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/properties'
23
+
24
+ module Glimmer
25
+ module SWT
26
+ module Custom
27
+ # Represents an animation declaratively
28
+ class Animation
29
+ include Properties # TODO rename to Properties
30
+
31
+ class << self
32
+ def schedule_frame_animation(animation, &frame_animation_block)
33
+ frame_animation_queue(animation).prepend(frame_animation_block)
34
+ swt_display.async_exec do
35
+ frame_animation_queue(next_animation)&.pop&.call
36
+ end
37
+ end
38
+
39
+ def next_animation
40
+ animation = nil
41
+ while frame_animation_queues.values.reduce(:+)&.any? && (animation.nil? || frame_animation_queue(animation).last.nil?)
42
+ animation = frame_animation_queues.keys[next_animation_index]
43
+ frame_animation_queues.delete(animation) if frame_animation_queues.values.reduce(:+)&.any? && !animation.nil? && frame_animation_queue(animation).empty?
44
+ end
45
+ animation
46
+ end
47
+
48
+ def next_animation_index
49
+ next_schedule_index % frame_animation_queues.keys.size
50
+ end
51
+
52
+ def next_schedule_index
53
+ unless defined? @@next_schedule_index
54
+ @@next_schedule_index = 0
55
+ else
56
+ @@next_schedule_index += 1
57
+ end
58
+ end
59
+
60
+ def frame_animation_queues
61
+ unless defined? @@frame_animation_queues
62
+ @@frame_animation_queues = {}
63
+ end
64
+ @@frame_animation_queues
65
+ end
66
+
67
+ def frame_animation_queue(animation)
68
+ frame_animation_queues[animation] ||= []
69
+ end
70
+
71
+ def swt_display
72
+ unless defined? @@swt_display
73
+ @@swt_display = DisplayProxy.instance.swt_display
74
+ end
75
+ @@swt_display
76
+ end
77
+ end
78
+
79
+ attr_reader :parent, :options, :frame_index, :cycle
80
+ alias current_frame_index frame_index
81
+ attr_accessor :frame_block, :every, :cycle_count, :frame_count, :started, :duration_limit
82
+ alias started? started
83
+ # TODO consider supporting an async: false option
84
+
85
+ def initialize(parent)
86
+ @parent = parent
87
+ @started = true
88
+ @frame_index = 0
89
+ @cycle_count_index = 0
90
+ @start_number = 0 # denotes the number of starts (increments on every start)
91
+ self.class.swt_display # ensures initializing variable to set from GUI thread
92
+ end
93
+
94
+ def post_add_content
95
+ @parent.on_widget_disposed { stop }
96
+ start if started?
97
+ end
98
+
99
+ # Starts an animation that is indefinite or has never been started before (i.e. having `started: false` option).
100
+ # Otherwise, resumes a stopped animation that has not been completed.
101
+ def start
102
+ return if @start_number > 0 && started?
103
+ @start_number += 1
104
+ @started = true
105
+ @start_time = Time.now
106
+ @original_start_time = @start_time if @duration.nil?
107
+ # TODO track when finished in a variable for finite animations (whether by frame count, cycle count, or duration limit)
108
+ Thread.new do
109
+ start_number = @start_number
110
+ if cycle_count.is_a?(Integer) && cycle.is_a?(Array)
111
+ (cycle_count * cycle.length).times do
112
+ break unless draw_frame(start_number)
113
+ end
114
+ else
115
+ loop do
116
+ # this code has to be duplicated to break from a loop (break keyword only works when literally in a loop block)
117
+ break unless draw_frame(start_number)
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ def stop
124
+ return if stopped?
125
+ @started = false
126
+ @duration = (Time.now - @start_time) + @duration.to_f if duration_limited? && !@start_time.nil?
127
+ end
128
+
129
+ # Restarts an animation (whether indefinite or not and whether stopped or not)
130
+ def restart
131
+ @original_start_time = @start_time = nil
132
+ @duration = nil
133
+ @frame_index = 0
134
+ @cycle_count_index = 0
135
+ stop
136
+ start
137
+ end
138
+
139
+ def stopped?
140
+ !started?
141
+ end
142
+
143
+ def finite?
144
+ frame_count_limited? || cycle_limited? || duration_limited?
145
+ end
146
+
147
+ def infinite?
148
+ !finite?
149
+ end
150
+ alias indefinite? infinite?
151
+
152
+ def has_attribute?(attribute_name, *args)
153
+ respond_to?(ruby_attribute_setter(attribute_name)) && respond_to?(ruby_attribute_getter(attribute_name))
154
+ end
155
+
156
+ def set_attribute(attribute_name, *args)
157
+ send(ruby_attribute_setter(attribute_name), *args)
158
+ end
159
+
160
+ def get_attribute(attribute_name)
161
+ send(ruby_attribute_getter(attribute_name))
162
+ end
163
+
164
+ def cycle=(*args)
165
+ if args.size == 1
166
+ if args.first.is_a?(Array)
167
+ @cycle = args.first
168
+ else
169
+ @cycle = [args.first]
170
+ end
171
+ elsif args.size > 1
172
+ @cycle = args
173
+ end
174
+ end
175
+
176
+ def cycle_enabled?
177
+ @cycle.is_a?(Array)
178
+ end
179
+
180
+ def cycle_limited?
181
+ cycle_enabled? && @cycle_count.is_a?(Integer)
182
+ end
183
+
184
+ def duration_limited?
185
+ @duration_limit.is_a?(Integer)
186
+ end
187
+
188
+ def frame_count_limited?
189
+ @frame_count.is_a?(Integer)
190
+ end
191
+
192
+ def surpassed_duration_limit?
193
+ duration_limited? && ((Time.now - @start_time) > (@duration_limit - @duration.to_f))
194
+ end
195
+
196
+ def within_duration_limit?
197
+ !surpassed_duration_limit?
198
+ end
199
+
200
+ private
201
+
202
+ # Returns true on success of painting a frame and false otherwise
203
+ def draw_frame(start_number)
204
+ return false if stopped? ||
205
+ start_number != @start_number ||
206
+ (frame_count_limited? && @frame_index == @frame_count) ||
207
+ (cycle_limited? && @cycle_count_index == @cycle_count) ||
208
+ surpassed_duration_limit?
209
+ block_args = [@frame_index]
210
+ block_args << @cycle[@frame_index % @cycle.length] if cycle_enabled?
211
+ current_frame_index = @frame_index
212
+ current_cycle_count_index = @cycle_count_index
213
+ self.class.schedule_frame_animation(self) do
214
+ if started? && start_number == @start_number && within_duration_limit?
215
+ @parent.clear_shapes
216
+ @parent.content {
217
+ frame_block.call(*block_args)
218
+ }
219
+ @parent.redraw
220
+ else
221
+ if stopped? && @frame_index > current_frame_index
222
+ @started = false
223
+ @frame_index = current_frame_index
224
+ @cycle_count_index = current_cycle_count_index
225
+ end
226
+ end
227
+ end
228
+ @frame_index += 1
229
+ @cycle_count_index += 1 if cycle_limited? && (@frame_index % @cycle&.length&.to_i) == 0
230
+ sleep(every) if every.is_a?(Numeric)
231
+ true
232
+ rescue => e
233
+ Glimmer::Config.logger.error {e}
234
+ false
235
+ end
236
+
237
+ end
238
+
239
+ end
240
+
241
+ end
242
+
243
+ end
@@ -61,11 +61,12 @@ module Glimmer
61
61
 
62
62
  before_body {
63
63
  @swt_style = swt_style == 0 ? [:border, :multi, :v_scroll, :h_scroll] : swt_style
64
+ @font_name = display.get_font_list(nil, true).map(&:name).include?('Consolas') ? 'Consolas' : 'Courier'
64
65
  }
65
66
 
66
67
  body {
67
68
  styled_text(swt_style) {
68
- font name: 'Consolas', height: 15
69
+ font name: @font_name, height: 15
69
70
  foreground rgb(75, 75, 75)
70
71
  left_margin 5
71
72
  top_margin 5
@@ -0,0 +1,43 @@
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
+ module Glimmer
23
+ module SWT
24
+ module Custom
25
+ # Represents SWT drawable controls (widgets like canvas) and display
26
+ module Drawable
27
+ def shapes
28
+ @shapes ||= []
29
+ end
30
+
31
+ def clear_shapes
32
+ shapes.dup.each do |shape|
33
+ shape.paint_listener_proxy&.unregister
34
+ shapes.delete(shape)
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,200 @@
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/properties'
23
+ require 'glimmer/swt/swt_proxy'
24
+ require 'glimmer/swt/display_proxy'
25
+ require 'glimmer/swt/color_proxy'
26
+ require 'glimmer/swt/font_proxy'
27
+
28
+ module Glimmer
29
+ module SWT
30
+ module Custom
31
+ # Represents a shape (graphics) to be drawn on a control/widget/canvas/display
32
+ # swt_widget returns the parent (e.g. a `canvas` WidgetProxy), equivalent to `parent.swt_widget`
33
+ # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
34
+ class Shape
35
+ include Packages
36
+ include Properties
37
+ # TODO support textExtent sized shapes nested within text/string
38
+ # TODO support a Pattern DSL for methods that take Pattern arguments
39
+
40
+ class << self
41
+ def valid?(parent, keyword, *args, &block)
42
+ gc_instance_methods.include?(method_name(keyword, args))
43
+ end
44
+
45
+ def gc_instance_methods
46
+ org.eclipse.swt.graphics.GC.instance_methods.map(&:to_s)
47
+ end
48
+
49
+ def arg_options(args, extract: false)
50
+ arg_options_method = extract ? :pop : :last
51
+ options = args.send(arg_options_method) if args.last.is_a?(Hash)
52
+ options.nil? ? {} : options.symbolize_keys
53
+ end
54
+
55
+ def method_name(keyword, args)
56
+ keyword = keyword.to_s
57
+ gradient = 'gradient_' if arg_options(args)[:gradient]
58
+ round = 'round_' if arg_options(args)[:round]
59
+ gc_instance_method_name_prefix = !['polyline', 'point', 'image', 'focus'].include?(keyword) && (arg_options(args)[:fill] || arg_options(args)[:gradient]) ? 'fill_' : 'draw_'
60
+ "#{gc_instance_method_name_prefix}#{gradient}#{round}#{keyword}"
61
+ end
62
+ end
63
+
64
+ attr_reader :parent, :name, :args, :options, :swt_widget, :paint_listener_proxy
65
+
66
+ def initialize(parent, keyword, *args, &property_block)
67
+ @parent = parent
68
+ @name = keyword
69
+ @method_name = self.class.method_name(keyword, args)
70
+ @options = self.class.arg_options(args, extract: true)
71
+ @args = args
72
+ @swt_widget = parent.respond_to?(:swt_display) ? parent.swt_display : parent.swt_widget
73
+ @properties = {}
74
+ @parent.shapes << self
75
+ post_add_content if property_block.nil?
76
+ end
77
+
78
+ def draw?
79
+ !fill?
80
+ end
81
+
82
+ def fill?
83
+ @options[:fill]
84
+ end
85
+
86
+ def gradient?
87
+ @options[:gradient]
88
+ end
89
+
90
+ def round?
91
+ @options[:round]
92
+ end
93
+
94
+ def post_add_content
95
+ event_handler = lambda do |event|
96
+ @properties['background'] = [@parent.background] if fill? && !@properties.keys.map(&:to_s).include?('background')
97
+ @properties['foreground'] = [@parent.foreground] if draw? && !@properties.keys.map(&:to_s).include?('foreground')
98
+ @properties.each do |property, args|
99
+ method_name = attribute_setter(property)
100
+ apply_property_arg_conversions(method_name, args)
101
+ event.gc.send(method_name, *args)
102
+ end
103
+ apply_shape_arg_conversions(@method_name, @args)
104
+ apply_shape_arg_defaults(@method_name, @args)
105
+ tolerate_shape_extra_args(@method_name, @args)
106
+ event.gc.send(@method_name, *@args)
107
+ end
108
+ if parent.respond_to?(:swt_display)
109
+ @paint_listener_proxy = @parent.on_swt_paint(&event_handler)
110
+ else
111
+ @paint_listener_proxy = @parent.on_paint_control(&event_handler)
112
+ end
113
+ end
114
+
115
+ def apply_property_arg_conversions(method_name, args)
116
+ the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
117
+ if (args.first.is_a?(Symbol) || args.first.is_a?(String))
118
+ if the_java_method.parameter_types.first == Color.java_class
119
+ args[0] = ColorProxy.new(args[0])
120
+ end
121
+ if the_java_method.parameter_types.first == Java::int.java_class
122
+ args[0] = SWTProxy.constant(args[0])
123
+ end
124
+ end
125
+ if args.first.is_a?(ColorProxy)
126
+ args[0] = args[0].swt_color
127
+ end
128
+ if args.first.is_a?(Hash) && the_java_method.parameter_types.first == Font.java_class
129
+ args[0] = FontProxy.new(args[0])
130
+ end
131
+ if args.first.is_a?(FontProxy)
132
+ args[0] = args[0].swt_font
133
+ end
134
+ if ['setBackgroundPattern', 'setForegroundPattern'].include?(method_name.to_s)
135
+ args.each_with_index do |arg, i|
136
+ if arg.is_a?(Symbol) || arg.is_a?(String)
137
+ args[i] = ColorProxy.new(arg).swt_color
138
+ elsif arg.is_a?(ColorProxy)
139
+ args[i] = arg.swt_color
140
+ end
141
+ end
142
+ new_args = [DisplayProxy.instance.swt_display] + args
143
+ args[0] = org.eclipse.swt.graphics.Pattern.new(*new_args)
144
+ args[1..-1] = []
145
+ end
146
+ end
147
+
148
+ def apply_shape_arg_conversions(method_name, args)
149
+ if args.size > 1 && (method_name.include?('polygon') || method_name.include?('polyline'))
150
+ args[0] = args.dup
151
+ args[1..-1] = []
152
+ end
153
+ end
154
+
155
+ def apply_shape_arg_defaults(method_name, args)
156
+ if method_name.include?('round_rectangle') && args.size.between?(4, 5)
157
+ (6 - args.size).times {args << 60}
158
+ elsif method_name.include?('rectangle') && gradient? && args.size == 4
159
+ args << true
160
+ elsif (method_name.include?('text') || method_name.include?('string')) && !@properties.keys.map(&:to_s).include?('background') && args.size == 3
161
+ args << true
162
+ end
163
+ if method_name.include?('image') && args.first.is_a?(String)
164
+ args[0] = ImageProxy.new(args[0])
165
+ end
166
+ if method_name.include?('image') && args.first.is_a?(ImageProxy)
167
+ args[0] = args[0].swt_image
168
+ end
169
+ end
170
+
171
+ # Tolerates shape extra args added by user by mistake
172
+ # (e.g. happens when switching from round rectangle to a standard one without removing all extra args)
173
+ def tolerate_shape_extra_args(method_name, args)
174
+ the_java_method_arg_count = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.select do |m|
175
+ m.name == method_name.camelcase(:lower)
176
+ end.map(&:parameter_types).map(&:size).max
177
+ if args.size > the_java_method_arg_count
178
+ args[the_java_method_arg_count..-1] = []
179
+ end
180
+ end
181
+
182
+ def has_attribute?(attribute_name, *args)
183
+ self.class.gc_instance_methods.include?(attribute_setter(attribute_name))
184
+ end
185
+
186
+ def set_attribute(attribute_name, *args)
187
+ @properties[attribute_name] = args
188
+ end
189
+
190
+ def get_attribute(attribute_name)
191
+ @properties.symbolize_keys[attribute_name.to_s.to_sym]
192
+ end
193
+
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+
200
+ end