glimmer-dsl-swt 4.18.4.4 → 4.18.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ ## Glimmer Style Guide
2
+
3
+ - Widgets are declared with underscored lowercase versions of their SWT names minus the SWT package name.
4
+ - Widget declarations may optionally have arguments and be followed by a block (to contain properties and content)
5
+ - Widget blocks are always declared with curly braces
6
+ - Widget arguments are always wrapped inside parentheses
7
+ - Widget properties are declared with underscored lowercase versions of the SWT properties
8
+ - Widget property declarations always have arguments and never take a block
9
+ - Widget property arguments are never wrapped inside parentheses
10
+ - Widget listeners are always declared starting with `on_` prefix and affixing listener event method name afterwards in underscored lowercase form
11
+ - Widget listeners are always followed by a block using curly braces (Only when declared in DSL. When invoked on widget object directly outside of GUI declarations, standard Ruby conventions apply)
12
+ - Data-binding is done via `bind` keyword, which always takes arguments wrapped in parentheses
13
+ - Custom widget body, before_body, and after_body blocks open their blocks and close them with curly braces.
14
+ - Custom widgets receive additional arguments to SWT style called options. These are passed as the last argument inside the parentheses, a hash of option names pointing to values.
@@ -2,11 +2,11 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-swt 4.18.4.4 ruby lib
5
+ # stub: glimmer-dsl-swt 4.18.4.5 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-swt".freeze
9
- s.version = "4.18.4.4"
9
+ s.version = "4.18.4.5"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
@@ -29,6 +29,13 @@ Gem::Specification.new do |s|
29
29
  "bin/girb",
30
30
  "bin/girb_runner.rb",
31
31
  "bin/glimmer",
32
+ "docs/reference/GLIMMER_COMMAND.md",
33
+ "docs/reference/GLIMMER_CONFIGURATION.md",
34
+ "docs/reference/GLIMMER_GIRB.md",
35
+ "docs/reference/GLIMMER_GUI_DSL_SYNTAX.md",
36
+ "docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md",
37
+ "docs/reference/GLIMMER_SAMPLES.md",
38
+ "docs/reference/GLIMMER_STYLE_GUIDE.md",
32
39
  "glimmer-dsl-swt.gemspec",
33
40
  "icons/scaffold_app.icns",
34
41
  "icons/scaffold_app.ico",
@@ -1,63 +1,63 @@
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'
23
- require 'glimmer/dsl/expression'
24
- require 'glimmer/dsl/parent_expression'
25
- require 'glimmer/dsl/top_level_expression'
26
- require 'glimmer/ui/custom_widget'
27
- require 'glimmer/ui/custom_shell'
28
- require 'glimmer/swt/custom/code_text'
29
- require 'glimmer/swt/custom/radio_group'
30
- require 'glimmer/swt/custom/checkbox_group'
31
-
32
- module Glimmer
33
- module DSL
34
- module SWT
35
- class CustomWidgetExpression < Expression
36
- # TODO Make custom widgets automatically generate static expressions
37
- include ParentExpression
38
- include TopLevelExpression
39
-
40
- def can_interpret?(parent, keyword, *args, &block)
41
- custom_widget_class = UI::CustomWidget.for(keyword)
42
- custom_widget_class and
43
- (parent.respond_to?(:swt_widget) or
44
- custom_widget_class.ancestors.include?(UI::CustomShell))
45
- end
46
-
47
- def interpret(parent, keyword, *args, &block)
48
- options = args.last.is_a?(Hash) ? args.pop : {}
49
- UI::CustomWidget.for(keyword).new(parent, *args, options, &block)
50
- end
51
-
52
- def add_content(parent, &block)
53
- # TODO consider avoiding source_location
54
- if block.source_location == parent.content&.__getobj__.source_location
55
- parent.content.call(parent) unless parent.content.called?
56
- else
57
- super
58
- end
59
- end
60
- end
61
- end
62
- end
63
- end
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'
23
+ require 'glimmer/dsl/expression'
24
+ require 'glimmer/dsl/parent_expression'
25
+ require 'glimmer/dsl/top_level_expression'
26
+ require 'glimmer/ui/custom_widget'
27
+ require 'glimmer/ui/custom_shell'
28
+ require 'glimmer/swt/custom/code_text'
29
+ require 'glimmer/swt/custom/radio_group'
30
+ require 'glimmer/swt/custom/checkbox_group'
31
+
32
+ module Glimmer
33
+ module DSL
34
+ module SWT
35
+ class CustomWidgetExpression < Expression
36
+ # TODO Make custom widgets automatically generate static expressions
37
+ include ParentExpression
38
+ include TopLevelExpression
39
+
40
+ def can_interpret?(parent, keyword, *args, &block)
41
+ custom_widget_class = UI::CustomWidget.for(keyword)
42
+ custom_widget_class and
43
+ (parent.respond_to?(:swt_widget) or
44
+ custom_widget_class.ancestors.include?(UI::CustomShell))
45
+ end
46
+
47
+ def interpret(parent, keyword, *args, &block)
48
+ options = args.last.is_a?(Hash) ? args.pop : {}
49
+ UI::CustomWidget.for(keyword).new(parent, *args, options, &block)
50
+ end
51
+
52
+ def add_content(parent, &block)
53
+ # TODO consider avoiding source_location
54
+ if block.source_location == parent.content&.__getobj__.source_location
55
+ parent.content.call(parent) unless parent.content.called?
56
+ else
57
+ super
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,55 +1,55 @@
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/top_level_expression'
23
- require 'glimmer/swt/display_proxy'
24
-
25
- module Glimmer
26
- module DSL
27
- module SWT
28
- # Mixin for common code in async_exec and sync_exec expressions
29
- # Uses name in subclass to auto-derive exec_operation
30
- module ExecExpression
31
- include TopLevelExpression
32
-
33
- def exec_operation
34
- @exec_operation ||= self.class.name.split(/::/).last.sub(/Expression$/, '').underscore
35
- end
36
-
37
- def can_interpret?(parent, keyword, *args, &block)
38
- keyword == exec_operation and
39
- block_given? and
40
- args.empty?
41
- end
42
-
43
- def interpret(parent, keyword, *args, &block)
44
- Glimmer::SWT::DisplayProxy.instance.swt_display.send(exec_operation) do |*args|
45
- begin
46
- block.call(*args)
47
- rescue => e
48
- Glimmer::Config.logger.error {e.full_message}
49
- end
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
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/top_level_expression'
23
+ require 'glimmer/swt/display_proxy'
24
+
25
+ module Glimmer
26
+ module DSL
27
+ module SWT
28
+ # Mixin for common code in async_exec and sync_exec expressions
29
+ # Uses name in subclass to auto-derive exec_operation
30
+ module ExecExpression
31
+ include TopLevelExpression
32
+
33
+ def exec_operation
34
+ @exec_operation ||= self.class.name.split(/::/).last.sub(/Expression$/, '').underscore
35
+ end
36
+
37
+ def can_interpret?(parent, keyword, *args, &block)
38
+ keyword == exec_operation and
39
+ block_given? and
40
+ args.empty?
41
+ end
42
+
43
+ def interpret(parent, keyword, *args, &block)
44
+ Glimmer::SWT::DisplayProxy.instance.swt_display.send(exec_operation, *args) do |*args|
45
+ begin
46
+ block.call(*args)
47
+ rescue => e
48
+ Glimmer::Config.logger.error {e.full_message}
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,53 +1,56 @@
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/static_expression'
23
- require 'glimmer/dsl/top_level_expression'
24
- require 'glimmer/data_binding/observer'
25
- require 'glimmer/data_binding/model_binding'
26
-
27
- module Glimmer
28
- module DSL
29
- module SWT
30
- class ObserveExpression < StaticExpression
31
- include TopLevelExpression
32
-
33
- REGEX_NESTED_OR_INDEXED_PROPERTY = /([^\[]+)(\[[^\]]+\])?/
34
-
35
- def can_interpret?(parent, keyword, *args, &block)
36
- keyword == 'observe' and
37
- block_given? and
38
- (args.size == 2) and
39
- textual?(args[1])
40
- end
41
-
42
- def interpret(parent, keyword, *args, &block)
43
- observer = DataBinding::Observer.proc(&block)
44
- if args[1].to_s.match(REGEX_NESTED_OR_INDEXED_PROPERTY)
45
- observer.observe(DataBinding::ModelBinding.new(args[0], args[1]))
46
- else
47
- observer.observe(args[0], args[1])
48
- end
49
- end
50
- end
51
- end
52
- end
53
- end
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/static_expression'
23
+ require 'glimmer/dsl/top_level_expression'
24
+ require 'glimmer/data_binding/observer'
25
+ require 'glimmer/data_binding/model_binding'
26
+ require 'glimmer/ui/custom_widget'
27
+
28
+ module Glimmer
29
+ module DSL
30
+ module SWT
31
+ class ObserveExpression < StaticExpression
32
+ include TopLevelExpression
33
+
34
+ REGEX_NESTED_OR_INDEXED_PROPERTY = /([^\[]+)(\[[^\]]+\])?/
35
+
36
+ def can_interpret?(parent, keyword, *args, &block)
37
+ keyword == 'observe' and
38
+ block_given? and
39
+ (args.size == 2) and
40
+ textual?(args[1])
41
+ end
42
+
43
+ def interpret(parent, keyword, *args, &block)
44
+ observer = DataBinding::Observer.proc(&block)
45
+ if args[1].to_s.match(REGEX_NESTED_OR_INDEXED_PROPERTY)
46
+ observer_registration = observer.observe(DataBinding::ModelBinding.new(args[0], args[1]))
47
+ else
48
+ observer_registration = observer.observe(args[0], args[1])
49
+ end
50
+ Glimmer::UI::CustomWidget.current_custom_widgets.last&.observer_registrations&.push(observer_registration)
51
+ observer_registration
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,243 +1,247 @@
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
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
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
+ if @dispose_listener_registration.nil?
96
+ @dispose_listener_registration = @parent.on_widget_disposed { stop }
97
+ start if started?
98
+ end
99
+ end
100
+
101
+ # Starts an animation that is indefinite or has never been started before (i.e. having `started: false` option).
102
+ # Otherwise, resumes a stopped animation that has not been completed.
103
+ def start
104
+ return if @start_number > 0 && started?
105
+ @start_number += 1
106
+ @started = true
107
+ @start_time = Time.now
108
+ @original_start_time = @start_time if @duration.nil?
109
+ # TODO track when finished in a variable for finite animations (whether by frame count, cycle count, or duration limit)
110
+ Thread.new do
111
+ start_number = @start_number
112
+ if cycle_count.is_a?(Integer) && cycle.is_a?(Array)
113
+ (cycle_count * cycle.length).times do
114
+ break unless draw_frame(start_number)
115
+ end
116
+ else
117
+ loop do
118
+ # this code has to be duplicated to break from a loop (break keyword only works when literally in a loop block)
119
+ break unless draw_frame(start_number)
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ def stop
126
+ return if stopped?
127
+ @started = false
128
+ @duration = (Time.now - @start_time) + @duration.to_f if duration_limited? && !@start_time.nil?
129
+ end
130
+
131
+ # Restarts an animation (whether indefinite or not and whether stopped or not)
132
+ def restart
133
+ @original_start_time = @start_time = nil
134
+ @duration = nil
135
+ @frame_index = 0
136
+ @cycle_count_index = 0
137
+ stop
138
+ start
139
+ end
140
+
141
+ def stopped?
142
+ !started?
143
+ end
144
+
145
+ def finite?
146
+ frame_count_limited? || cycle_limited? || duration_limited?
147
+ end
148
+
149
+ def infinite?
150
+ !finite?
151
+ end
152
+ alias indefinite? infinite?
153
+
154
+ def has_attribute?(attribute_name, *args)
155
+ respond_to?(ruby_attribute_setter(attribute_name)) && respond_to?(ruby_attribute_getter(attribute_name))
156
+ end
157
+
158
+ def set_attribute(attribute_name, *args)
159
+ send(ruby_attribute_setter(attribute_name), *args)
160
+ end
161
+
162
+ def get_attribute(attribute_name)
163
+ send(ruby_attribute_getter(attribute_name))
164
+ end
165
+
166
+ def cycle=(*args)
167
+ if args.size == 1
168
+ if args.first.is_a?(Array)
169
+ @cycle = args.first
170
+ else
171
+ @cycle = [args.first]
172
+ end
173
+ elsif args.size > 1
174
+ @cycle = args
175
+ end
176
+ end
177
+
178
+ def cycle_enabled?
179
+ @cycle.is_a?(Array)
180
+ end
181
+
182
+ def cycle_limited?
183
+ cycle_enabled? && @cycle_count.is_a?(Integer)
184
+ end
185
+
186
+ def duration_limited?
187
+ @duration_limit.is_a?(Integer)
188
+ end
189
+
190
+ def frame_count_limited?
191
+ @frame_count.is_a?(Integer)
192
+ end
193
+
194
+ def surpassed_duration_limit?
195
+ duration_limited? && ((Time.now - @start_time) > (@duration_limit - @duration.to_f))
196
+ end
197
+
198
+ def within_duration_limit?
199
+ !surpassed_duration_limit?
200
+ end
201
+
202
+ private
203
+
204
+ # Returns true on success of painting a frame and false otherwise
205
+ def draw_frame(start_number)
206
+ return false if stopped? ||
207
+ start_number != @start_number ||
208
+ (frame_count_limited? && @frame_index == @frame_count) ||
209
+ (cycle_limited? && @cycle_count_index == @cycle_count) ||
210
+ surpassed_duration_limit?
211
+ block_args = [@frame_index]
212
+ block_args << @cycle[@frame_index % @cycle.length] if cycle_enabled?
213
+ current_frame_index = @frame_index
214
+ current_cycle_count_index = @cycle_count_index
215
+ self.class.schedule_frame_animation(self) do
216
+ if started? && start_number == @start_number && within_duration_limit?
217
+ unless @parent.isDisposed
218
+ @parent.clear_shapes
219
+ @parent.content {
220
+ frame_block.call(*block_args)
221
+ }
222
+ @parent.redraw
223
+ end
224
+ else
225
+ if stopped? && @frame_index > current_frame_index
226
+ @started = false
227
+ @frame_index = current_frame_index
228
+ @cycle_count_index = current_cycle_count_index
229
+ end
230
+ end
231
+ end
232
+ @frame_index += 1
233
+ @cycle_count_index += 1 if cycle_limited? && (@frame_index % @cycle&.length&.to_i) == 0
234
+ sleep(every) if every.is_a?(Numeric)
235
+ true
236
+ rescue => e
237
+ Glimmer::Config.logger.error {e}
238
+ false
239
+ end
240
+
241
+ end
242
+
243
+ end
244
+
245
+ end
246
+
247
+ end