glimmer-dsl-swt 4.18.1.0 → 4.18.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -30,7 +30,8 @@ module Glimmer
30
30
  args.size > 0 and
31
31
  parent.respond_to?(:set_attribute) and
32
32
  parent.respond_to?(:has_attribute?) and
33
- parent.has_attribute?(keyword, *args)
33
+ parent.has_attribute?(keyword, *args) and
34
+ !(parent.respond_to?(:swt_widget) && parent.swt_widget.class == org.eclipse.swt.widgets.Canvas && keyword == 'image')
34
35
  end
35
36
 
36
37
  def interpret(parent, keyword, *args, &block)
@@ -39,7 +39,7 @@ module Glimmer
39
39
  end
40
40
 
41
41
  def interpret(parent, keyword, *args, &block)
42
- Glimmer::SWT::Custom::Shape.new(parent, keyword, *args)
42
+ Glimmer::SWT::Custom::Shape.new(parent, keyword, *args, &block)
43
43
  end
44
44
 
45
45
  def add_content(parent, &block)
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -57,11 +57,11 @@ module Glimmer
57
57
  lines = `gem search -d #{gem_prefix}`.split("\n")
58
58
  gems = lines.slice_before {|l| l.match(REGEX_GEM_LINE) }.to_a
59
59
  gems = gems.map do |gem|
60
- {
60
+ {
61
61
  name: gem[0].match(REGEX_GEM_LINE)[1],
62
62
  version: gem[0].match(REGEX_GEM_LINE)[2],
63
63
  author: gem[1].strip,
64
- description: gem[4..-1].map(&:strip).join(' ')
64
+ description: gem[4..-1]&.map(&:strip)&.join(' ').to_s
65
65
  }
66
66
  end.select do |gem|
67
67
  query.nil? || "#{gem[:name]} #{gem[:author]} #{gem[:description]}".downcase.include?(query.to_s.downcase)
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -27,7 +27,7 @@ module Glimmer
27
27
  module Package
28
28
  class << self
29
29
  attr_accessor :javapackager_extra_args
30
- alias jpackage_extra_args :javapackager_extra_args
30
+ alias jpackage_extra_args javapackager_extra_args
31
31
 
32
32
  def clean
33
33
  require 'fileutils'
@@ -48,7 +48,7 @@ module Glimmer
48
48
  FileUtils.mkdir_p('vendor/jars')
49
49
  command = "lock_jars --vendor-dir vendor/jars"
50
50
  puts command
51
- system command
51
+ system command
52
52
  end
53
53
 
54
54
  def config
@@ -78,18 +78,18 @@ module Glimmer
78
78
  else
79
79
  puts 'Warbler executable "warble" is missing!'
80
80
  end
81
- end
81
+ end
82
82
  end
83
83
 
84
84
  def jar
85
85
  FileUtils.mkdir_p('dist')
86
86
  puts "Generating JAR with Warbler..."
87
87
  system "jruby -S gem install warbler -v2.0.5 --no-document" unless warbler_exists?
88
- system('warble')
88
+ system('warble')
89
89
  end
90
90
 
91
91
  def native(native_type=nil, native_extra_args)
92
- puts "Generating native executable with javapackager/jpackage..."
92
+ puts "Generating native executable with javapackager/jpackage..."
93
93
  java_version = `java -version`
94
94
  puts "WARNING! Glimmer Packaging Pre-Requisite Java Version 1.8.0_241 Is Not Found!" unless java_version.include?('1.8.0_241')
95
95
  require 'facets/string/titlecase'
@@ -125,7 +125,7 @@ module Glimmer
125
125
  command += " #{ENV['JAVAPACKAGER_EXTRA_ARGS']} " if ENV['JAVAPACKAGER_EXTRA_ARGS']
126
126
  command += " #{native_extra_args} " if native_extra_args
127
127
  puts command
128
- system command
128
+ system command
129
129
  end
130
130
 
131
131
  private
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -42,6 +42,7 @@ module Glimmer
42
42
  *.gem
43
43
  *.rbc
44
44
  /.config
45
+ /.mvn/
45
46
  /coverage/
46
47
  /InstalledFiles
47
48
  /pkg/
@@ -97,9 +98,9 @@ module Glimmer
97
98
  .gladiator
98
99
 
99
100
  # Glimmer
100
- dist
101
- packages
102
- vendor/jars
101
+ /dist/
102
+ /packages/
103
+ /vendor/jars/
103
104
  MULTI_LINE_STRING
104
105
 
105
106
  GEMFILE = <<~MULTI_LINE_STRING
@@ -113,7 +114,6 @@ module Glimmer
113
114
 
114
115
  group :development do
115
116
  gem 'rspec', '~> 3.5.0'
116
- gem 'git-glimmer', '1.7.0'
117
117
  gem 'juwelier', '2.4.9'
118
118
  gem 'warbler', '2.0.5'
119
119
  gem 'simplecov', '>= 0'
@@ -133,12 +133,12 @@ module Glimmer
133
133
  gem_summary = human_name(app_name)
134
134
  return puts("The directory '#{gem_name}' already exists. Please either remove or pick a different name.") if Dir.exist?(gem_name)
135
135
  system "jruby -S gem install juwelier -v2.4.9 --no-document" unless juwelier_exists?
136
- system "jruby -r git-glimmer -S juwelier --markdown --rspec --summary '#{gem_summary}' --description '#{gem_summary}' #{gem_name}"
136
+ system "jruby -S juwelier --markdown --rspec --summary '#{gem_summary}' --description '#{gem_summary}' #{gem_name}"
137
137
  return puts('Your Git user.name and/or github.user are missing! Please add in for Juwelier to help Glimmer with Scaffolding.') if `git config --get github.user`.strip.empty? && `git config --get user.name`.strip.empty?
138
138
  cd gem_name
139
139
  rm_rf 'lib'
140
140
  write '.gitignore', GITIGNORE
141
- write '.ruby-version', RUBY_VERSION
141
+ write '.ruby-version', RUBY_VERSION
142
142
  write '.ruby-gemset', app_name
143
143
  write 'VERSION', '1.0.0'
144
144
  write 'LICENSE.txt', "Copyright (c) #{Time.now.year} #{app_name}"
@@ -178,7 +178,7 @@ module Glimmer
178
178
  system "bundle"
179
179
  system "rspec --init"
180
180
  else
181
- system "bash -c '#{RVM_FUNCTION}\n cd .\n bundle\n rspec --init\n'"
181
+ system "bash -c '#{RVM_FUNCTION}\n cd .\n bundle\n rspec --init\n'"
182
182
  end
183
183
  write 'spec/spec_helper.rb', spec_helper_file
184
184
  if OS.windows?
@@ -191,7 +191,7 @@ module Glimmer
191
191
  else
192
192
  system "glimmer run"
193
193
  end
194
- end
194
+ end
195
195
  end
196
196
 
197
197
  def custom_shell(custom_shell_name, namespace, shell_type = nil, shell_options = {})
@@ -231,11 +231,11 @@ module Glimmer
231
231
  end
232
232
  return puts("The directory '#{gem_name}' already exists. Please either remove or pick a different name.") if Dir.exist?(gem_name)
233
233
  system "jruby -S gem install juwelier -v2.4.9 --no-document" unless juwelier_exists?
234
- system "jruby -r git-glimmer -S juwelier --markdown --rspec --summary '#{gem_summary}' --description '#{gem_summary}' #{gem_name}"
234
+ system "jruby -S juwelier --markdown --rspec --summary '#{gem_summary}' --description '#{gem_summary}' #{gem_name}"
235
235
  return puts('Your Git user.name and/or github.user are missing! Please add in for Juwelier to help Glimmer with Scaffolding.') if `git config --get github.user`.strip.empty? && `git config --get user.name`.strip.empty?
236
236
  cd gem_name
237
237
  write '.gitignore', GITIGNORE
238
- write '.ruby-version', RUBY_VERSION
238
+ write '.ruby-version', RUBY_VERSION
239
239
  write '.ruby-gemset', gem_name
240
240
  write 'VERSION', '1.0.0'
241
241
  write 'Gemfile', GEMFILE
@@ -256,7 +256,7 @@ module Glimmer
256
256
  system "rspec --init"
257
257
  else
258
258
  system "bash -c '#{RVM_FUNCTION}\n cd .\n bundle\n rspec --init\n'"
259
- end
259
+ end
260
260
  write 'spec/spec_helper.rb', spec_helper_file
261
261
 
262
262
  mkdir_p 'package/windows'
@@ -283,7 +283,7 @@ module Glimmer
283
283
  system "open packages/bundles/#{human_name(custom_shell_name).gsub(' ', '\ ')}.app" if OS.mac?
284
284
  else
285
285
  system "glimmer run"
286
- end
286
+ end
287
287
  end
288
288
  puts "Finished creating #{gem_name} Ruby gem."
289
289
  puts 'Edit Rakefile to configure gem details.'
@@ -305,11 +305,11 @@ module Glimmer
305
305
 
306
306
  return puts("The directory '#{gem_name}' already exists. Please either remove or pick a different name.") if Dir.exist?(gem_name)
307
307
  system "jruby -S gem install juwelier -v2.4.9 --no-document" unless juwelier_exists?
308
- system "jruby -r git-glimmer -S juwelier --markdown --rspec --summary '#{gem_summary}' --description '#{gem_summary}' #{gem_name}"
308
+ system "jruby -S juwelier --markdown --rspec --summary '#{gem_summary}' --description '#{gem_summary}' #{gem_name}"
309
309
  return puts('Your Git user.name and/or github.user are missing! Please add in for Juwelier to help Glimmer with Scaffolding.') if `git config --get github.user`.strip.empty? && `git config --get user.name`.strip.empty?
310
310
  cd gem_name
311
311
  write '.gitignore', GITIGNORE
312
- write '.ruby-version', RUBY_VERSION
312
+ write '.ruby-version', RUBY_VERSION
313
313
  write '.ruby-gemset', gem_name
314
314
  write 'VERSION', '1.0.0'
315
315
  write 'Gemfile', GEMFILE
@@ -397,9 +397,9 @@ module Glimmer
397
397
  class #{class_name(app_name)}
398
398
  include Glimmer
399
399
 
400
- APP_ROOT = File.expand_path('../..', __FILE__)
400
+ APP_ROOT = File.expand_path('../..', __FILE__)
401
401
  VERSION = File.read(File.join(APP_ROOT, 'VERSION'))
402
- LICENSE = File.read(File.join(APP_ROOT, 'LICENSE.txt'))
402
+ LICENSE = File.read(File.join(APP_ROOT, 'LICENSE.txt'))
403
403
 
404
404
  def open
405
405
  app_view.open
@@ -443,7 +443,7 @@ module Glimmer
443
443
 
444
444
  launcher = Glimmer::Launcher.new([runner] + ARGV)
445
445
  launcher.launch
446
- end
446
+ end
447
447
  MULTI_LINE_STRING
448
448
  end
449
449
 
@@ -482,7 +482,7 @@ module Glimmer
482
482
 
483
483
  launcher = Glimmer::Launcher.new([runner] + ARGV)
484
484
  launcher.launch
485
- end
485
+ end
486
486
  MULTI_LINE_STRING
487
487
  end
488
488
 
@@ -502,15 +502,15 @@ module Glimmer
502
502
  end
503
503
  spec_pattern_line_index = lines.index(lines.detect {|l| l.include?('spec.pattern =') })
504
504
  lines.insert(spec_pattern_line_index+1, " spec.ruby_opts = [Glimmer::Launcher.jruby_os_specific_options]")
505
- lines << "\nrequire 'glimmer/rake_task'\n"
505
+ lines << "\nrequire 'glimmer/rake_task'\n"
506
506
  file_content = lines.join("\n")
507
507
  if custom_shell_name
508
- file_content << <<~MULTI_LINE_STRING
508
+ file_content << <<~MULTI_LINE_STRING
509
509
  Glimmer::RakeTask::Package.javapackager_extra_args =
510
510
  " -name '#{human_name(custom_shell_name)}'" +
511
511
  " -title '#{human_name(custom_shell_name)}'" +
512
512
  " -Bmac.CFBundleName='#{human_name(custom_shell_name)}'" +
513
- " -Bmac.CFBundleIdentifier='org.#{namespace ? compact_name(namespace) : compact_name(custom_shell_name)}.application.#{compact_name(custom_shell_name).camelcase(:upper)}'"
513
+ " -Bmac.CFBundleIdentifier='org.#{namespace ? compact_name(namespace) : compact_name(custom_shell_name)}.application.#{compact_name(custom_shell_name).camelcase(:upper)}'"
514
514
  # " -BlicenseType=" +
515
515
  # " -Bmac.category=" +
516
516
  # " -Bmac.signing-key-developer-id-app="
@@ -575,8 +575,8 @@ module Glimmer
575
575
  if %i[gem app desktopify].include?(shell_type)
576
576
  custom_shell_file_content += <<-MULTI_LINE_STRING
577
577
  before_body {
578
- Display.setAppName('#{shell_type == :gem ? human_name(custom_shell_name) : human_name(namespace)}')
579
- Display.setAppVersion(VERSION)
578
+ Display.app_name = '#{shell_type == :gem ? human_name(custom_shell_name) : human_name(namespace)}'
579
+ Display.app_version = VERSION
580
580
  @display = display {
581
581
  on_about {
582
582
  display_about_dialog
@@ -600,7 +600,7 @@ module Glimmer
600
600
  ## Use after_body block to setup observers for widgets in body
601
601
  #
602
602
  # after_body {
603
- #
603
+ #
604
604
  # }
605
605
 
606
606
  ## Add widget content inside custom shell body
@@ -616,10 +616,10 @@ module Glimmer
616
616
  MULTI_LINE_STRING
617
617
 
618
618
  if shell_type == :desktopify
619
- custom_shell_file_content += <<-MULTI_LINE_STRING
619
+ custom_shell_file_content += <<-MULTI_LINE_STRING
620
620
  browser {
621
- url "#{shell_options[:website]}"
622
- }
621
+ url "#{shell_options[:website]}"
622
+ }
623
623
  MULTI_LINE_STRING
624
624
  else
625
625
  custom_shell_file_content += <<-MULTI_LINE_STRING
@@ -628,7 +628,7 @@ module Glimmer
628
628
  text bind(self, :greeting)
629
629
  font height: 40
630
630
  layout_data :fill, :center, true, true
631
- }
631
+ }
632
632
  MULTI_LINE_STRING
633
633
  end
634
634
 
@@ -690,7 +690,7 @@ module Glimmer
690
690
  text 'Greeting'
691
691
  font style: :bold
692
692
  [
693
- 'Hello, World!',
693
+ 'Hello, World!',
694
694
  'Howdy, Partner!'
695
695
  ].each do |greeting_text|
696
696
  button(:radio) {
@@ -733,19 +733,19 @@ module Glimmer
733
733
  #
734
734
  #
735
735
  # before_body {
736
- #
736
+ #
737
737
  # }
738
738
 
739
739
  ## Use after_body block to setup observers for widgets in body
740
740
  #
741
741
  # after_body {
742
- #
742
+ #
743
743
  # }
744
744
 
745
745
  ## Add widget content under custom widget body
746
746
  ##
747
- ## If you want to add a shell as the top-most widget,
748
- ## consider creating a custom shell instead
747
+ ## If you want to add a shell as the top-most widget,
748
+ ## consider creating a custom shell instead
749
749
  ## (Glimmer::UI::CustomShell offers shell convenience methods, like show and hide)
750
750
  #
751
751
  body {
@@ -28,68 +28,127 @@ module Glimmer
28
28
  class Animation
29
29
  include Properties # TODO rename to Properties
30
30
 
31
- attr_reader :parent, :options, :frame_index
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
32
80
  alias current_frame_index frame_index
33
- attr_accessor :frame_block, :every, :cycle, :cycle_count, :frame_count, :started
81
+ attr_accessor :frame_block, :every, :cycle_count, :frame_count, :started, :duration_limit
82
+ alias started? started
34
83
  # TODO consider supporting an async: false option
35
84
 
36
85
  def initialize(parent)
37
86
  @parent = parent
38
87
  @started = true
39
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
40
92
  end
41
93
 
42
94
  def post_add_content
43
95
  @parent.on_widget_disposed { stop }
44
- start if @started
96
+ start if started?
45
97
  end
46
-
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.
47
101
  def start
48
- swt_display = Glimmer::SWT::DisplayProxy.instance.swt_display
49
- return if swt_display.is_disposed?
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)
50
108
  Thread.new do
51
- frame_rendering_block = lambda do
52
- block_args = [@frame_index]
53
- block_args << @cycle[@frame_index % @cycle.length] if @cycle.is_a?(Array)
54
- swt_display.async_exec do
55
- @parent.clear_shapes
56
- @parent.content {
57
- frame_block.call(*block_args)
58
- }
59
- end
60
- @frame_index += 1
61
- sleep(every) if every.is_a?(Numeric)
62
- end
63
-
109
+ start_number = @start_number
64
110
  if cycle_count.is_a?(Integer) && cycle.is_a?(Array)
65
111
  (cycle_count * cycle.length).times do
66
- break if break_condition?
67
- begin
68
- frame_rendering_block.call
69
- rescue => e
70
- Glimmer::Config.logger.error {e}
71
- break
72
- end
112
+ break unless draw_frame(start_number)
73
113
  end
74
114
  else
75
115
  loop do
76
- break if break_condition?
77
- begin
78
- frame_rendering_block.call
79
- rescue => e
80
- Glimmer::Config.logger.error {e}
81
- break
82
- end
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)
83
118
  end
84
119
  end
85
- @started = false
86
120
  end
87
121
  end
88
122
 
89
123
  def stop
124
+ return if stopped?
90
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?
91
141
  end
92
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
+
93
152
  def has_attribute?(attribute_name, *args)
94
153
  respond_to?(ruby_attribute_setter(attribute_name)) && respond_to?(ruby_attribute_getter(attribute_name))
95
154
  end
@@ -102,11 +161,79 @@ module Glimmer
102
161
  send(ruby_attribute_getter(attribute_name))
103
162
  end
104
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
+
105
200
  private
106
201
 
107
- def break_condition?
108
- !@started || (@frame_count.is_a?(Integer) && @frame_index == @frame_count)
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
109
235
  end
236
+
110
237
  end
111
238
 
112
239
  end