mirror of https://github.com/weisJ/darklaf.git
weisj
5 years ago
1 changed files with 181 additions and 0 deletions
@ -0,0 +1,181 @@ |
|||||||
|
package com.weis.darklaf.util; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import javax.swing.*; |
||||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||||
|
import java.util.concurrent.ScheduledFuture; |
||||||
|
import java.util.concurrent.ScheduledThreadPoolExecutor; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import java.util.concurrent.atomic.AtomicBoolean; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author Konstantin Bulenkov |
||||||
|
*/ |
||||||
|
public abstract class Animator { |
||||||
|
private final static ScheduledExecutorService scheduler = createScheduler(); |
||||||
|
private final int totalFrames; |
||||||
|
private final int cycleDuration; |
||||||
|
private final boolean forward; |
||||||
|
private final boolean repeatable; |
||||||
|
private ScheduledFuture<?> ticker; |
||||||
|
private int startFrame; |
||||||
|
private int currentFrame; |
||||||
|
private long startTime; |
||||||
|
private long stopTime; |
||||||
|
private volatile boolean disposed = false; |
||||||
|
|
||||||
|
public Animator(final String name, |
||||||
|
final int totalFrames, |
||||||
|
final int cycleDuration, |
||||||
|
final boolean repeatable) { |
||||||
|
|
||||||
|
this(name, totalFrames, cycleDuration, repeatable, true); |
||||||
|
} |
||||||
|
|
||||||
|
public Animator(final String name, |
||||||
|
final int totalFrames, |
||||||
|
final int cycleDuration, |
||||||
|
final boolean repeatable, |
||||||
|
final boolean forward) { |
||||||
|
this.totalFrames = totalFrames; |
||||||
|
this.cycleDuration = cycleDuration; |
||||||
|
this.repeatable = repeatable; |
||||||
|
this.forward = forward; |
||||||
|
currentFrame = forward ? 0 : totalFrames; |
||||||
|
|
||||||
|
reset(); |
||||||
|
} |
||||||
|
|
||||||
|
@NotNull |
||||||
|
private static ScheduledExecutorService createScheduler() { |
||||||
|
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, r -> { |
||||||
|
final Thread thread = new Thread(r, "Darcula Animations"); |
||||||
|
thread.setDaemon(true); |
||||||
|
thread.setPriority(Thread.NORM_PRIORITY); |
||||||
|
return thread; |
||||||
|
}); |
||||||
|
executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); |
||||||
|
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); |
||||||
|
return executor; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private void onTick() { |
||||||
|
if (isDisposed()) return; |
||||||
|
|
||||||
|
if (startTime == -1) { |
||||||
|
startTime = System.currentTimeMillis(); |
||||||
|
stopTime = startTime + cycleDuration * (totalFrames - currentFrame) / totalFrames; |
||||||
|
} |
||||||
|
|
||||||
|
final double passedTime = System.currentTimeMillis() - startTime; |
||||||
|
final double totalTime = stopTime - startTime; |
||||||
|
|
||||||
|
final int newFrame = (int) (passedTime * totalFrames / totalTime) + startFrame; |
||||||
|
if (currentFrame > 0 && newFrame == currentFrame) return; |
||||||
|
currentFrame = newFrame; |
||||||
|
|
||||||
|
if (currentFrame >= totalFrames) { |
||||||
|
if (repeatable) { |
||||||
|
reset(); |
||||||
|
} else { |
||||||
|
animationDone(); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
paint(); |
||||||
|
} |
||||||
|
|
||||||
|
private void paint() { |
||||||
|
paintNow(forward ? currentFrame : totalFrames - currentFrame - 1, totalFrames, cycleDuration); |
||||||
|
} |
||||||
|
|
||||||
|
private void animationDone() { |
||||||
|
stopTicker(); |
||||||
|
|
||||||
|
SwingUtilities.invokeLater(this::paintCycleEnd); |
||||||
|
} |
||||||
|
|
||||||
|
private void stopTicker() { |
||||||
|
if (ticker != null) { |
||||||
|
ticker.cancel(false); |
||||||
|
ticker = null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void paintCycleEnd() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void suspend() { |
||||||
|
startTime = -1; |
||||||
|
stopTicker(); |
||||||
|
} |
||||||
|
|
||||||
|
public void resume() { |
||||||
|
resume(0); |
||||||
|
} |
||||||
|
|
||||||
|
public void resume(final int startFrame) { |
||||||
|
if (startFrame < 0) { |
||||||
|
throw new IllegalArgumentException("Starting frame must be non negative."); |
||||||
|
} |
||||||
|
if (cycleDuration == 0 || startFrame >= totalFrames) { |
||||||
|
currentFrame = totalFrames - 1; |
||||||
|
paint(); |
||||||
|
animationDone(); |
||||||
|
} else if (ticker == null) { |
||||||
|
this.startFrame = startFrame; |
||||||
|
ticker = scheduler.scheduleWithFixedDelay(new Runnable() { |
||||||
|
final AtomicBoolean isScheduled = new AtomicBoolean(false); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (isScheduled.compareAndSet(false, true) && !isDisposed()) { |
||||||
|
SwingUtilities.invokeLater(() -> { |
||||||
|
isScheduled.set(false); |
||||||
|
onTick(); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
}, 0, cycleDuration * 1000 / totalFrames, TimeUnit.MICROSECONDS); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public abstract void paintNow(int frame, int totalFrames, int cycle); |
||||||
|
|
||||||
|
public void dispose() { |
||||||
|
disposed = true; |
||||||
|
stopTicker(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isRunning() { |
||||||
|
return ticker != null; |
||||||
|
} |
||||||
|
|
||||||
|
public void reset() { |
||||||
|
currentFrame = 0; |
||||||
|
startTime = -1; |
||||||
|
} |
||||||
|
|
||||||
|
@Contract(pure = true) |
||||||
|
public final boolean isForward() { |
||||||
|
return forward; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isDisposed() { |
||||||
|
return disposed; |
||||||
|
} |
||||||
|
|
||||||
|
public int getCurrentFrame() { |
||||||
|
return currentFrame; |
||||||
|
} |
||||||
|
|
||||||
|
public float getTotalFrames() { |
||||||
|
return totalFrames; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue