public code v1
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>flintstones.group</groupId>
|
||||
<artifactId>flintstones.bundles</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>org.pushingpixels.trident.swt</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
<name>[bundle] Swt</name>
|
||||
</project>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.pushingpixels.trident.swt</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
<filteredResources>
|
||||
<filter>
|
||||
<id>1779484362764</id>
|
||||
<name></name>
|
||||
<type>30</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
||||
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
</filteredResources>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
@@ -0,0 +1,7 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
@@ -0,0 +1,4 @@
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
@@ -0,0 +1,9 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Swt
|
||||
Bundle-SymbolicName: org.pushingpixels.trident.swt
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Automatic-Module-Name: org.pushingpixels.trident.swt
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: org.eclipse.swt
|
||||
Export-Package: org.pushingpixels.trident
|
||||
@@ -0,0 +1,4 @@
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
public interface PropertyGetter<T> {
|
||||
public T get(Object obj, String fieldName);
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
public interface PropertySetter<T> {
|
||||
public void set(Object obj, String fieldName, T value);
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Added method "setPropertyValues" - Loris Securo for CustomButton
|
||||
*/
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.pushingpixels.trident.TimelineEngine.FullObjectID;
|
||||
import org.pushingpixels.trident.TimelineEngine.TimelineOperationKind;
|
||||
import org.pushingpixels.trident.TimelinePropertyBuilder.AbstractFieldInfo;
|
||||
import org.pushingpixels.trident.callback.RunOnUIThread;
|
||||
import org.pushingpixels.trident.callback.TimelineCallback;
|
||||
import org.pushingpixels.trident.callback.TimelineCallbackAdapter;
|
||||
import org.pushingpixels.trident.ease.Linear;
|
||||
import org.pushingpixels.trident.ease.TimelineEase;
|
||||
import org.pushingpixels.trident.interpolator.KeyFrames;
|
||||
@SuppressWarnings({"rawtypes","unchecked"})
|
||||
public class Timeline implements TimelineScenario.TimelineScenarioActor {
|
||||
Object mainObject;
|
||||
|
||||
Comparable<?> secondaryId;
|
||||
|
||||
FullObjectID fullObjectID;
|
||||
|
||||
long duration;
|
||||
|
||||
long initialDelay;
|
||||
|
||||
long cycleDelay;
|
||||
|
||||
boolean isLooping;
|
||||
|
||||
int repeatCount;
|
||||
|
||||
RepeatBehavior repeatBehavior;
|
||||
|
||||
UIToolkitHandler uiToolkitHandler;
|
||||
|
||||
Chain callback;
|
||||
|
||||
String name;
|
||||
|
||||
List<AbstractFieldInfo> propertiesToInterpolate;
|
||||
|
||||
/**
|
||||
* Is used to create unique value for the {@link #id} field.
|
||||
*/
|
||||
static long counter;
|
||||
|
||||
/**
|
||||
* Unique ID.
|
||||
*/
|
||||
protected long id;
|
||||
|
||||
/**
|
||||
* Timeline position.
|
||||
*/
|
||||
float durationFraction;
|
||||
|
||||
/**
|
||||
* Timeline position.
|
||||
*/
|
||||
float timelinePosition;
|
||||
|
||||
long timeUntilPlay;
|
||||
|
||||
/**
|
||||
* Indication whether the looping timeline should stop at reaching the end
|
||||
* of the cycle. Relevant only when {@link #isLooping} is <code>true</code>.
|
||||
*/
|
||||
boolean toCancelAtCycleBreak;
|
||||
|
||||
Stack<TimelineState> stateStack;
|
||||
|
||||
TimelineEase ease;
|
||||
|
||||
private int doneCount;
|
||||
|
||||
public enum RepeatBehavior {
|
||||
LOOP, REVERSE
|
||||
}
|
||||
|
||||
public enum TimelineState {
|
||||
IDLE(false), READY(false), PLAYING_FORWARD(true), PLAYING_REVERSE(true), SUSPENDED(
|
||||
false), CANCELLED(false), DONE(false);
|
||||
|
||||
private boolean isActive;
|
||||
|
||||
private TimelineState(boolean isActive) {
|
||||
this.isActive = isActive;
|
||||
}
|
||||
}
|
||||
|
||||
private class Setter extends TimelineCallbackAdapter {
|
||||
@Override
|
||||
public void onTimelineStateChanged(TimelineState oldState,
|
||||
TimelineState newState, float durationFraction,
|
||||
float timelinePosition) {
|
||||
if (newState == TimelineState.READY) {
|
||||
for (AbstractFieldInfo fInfo : propertiesToInterpolate) {
|
||||
// check whether the object is in the ready state
|
||||
if ((uiToolkitHandler != null)
|
||||
&& !uiToolkitHandler.isInReadyState(fInfo.object)) {
|
||||
continue;
|
||||
}
|
||||
fInfo.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
// Fix for issue 5 - update field values only when
|
||||
// either old or new state (or both) are active. Otherwise
|
||||
// it's a transition between inactive states (such as from
|
||||
// DONE to IDLE) that shouldn't trigger the property changes
|
||||
if (oldState.isActive || newState.isActive) {
|
||||
for (AbstractFieldInfo fInfo : propertiesToInterpolate) {
|
||||
// check whether the object is in the ready state
|
||||
if ((uiToolkitHandler != null)
|
||||
&& !uiToolkitHandler.isInReadyState(fInfo.object)) {
|
||||
continue;
|
||||
}
|
||||
fInfo.updateFieldValue(timelinePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelinePulse(float durationFraction,
|
||||
float timelinePosition) {
|
||||
for (AbstractFieldInfo fInfo : propertiesToInterpolate) {
|
||||
// check whether the object is in the ready state
|
||||
if ((uiToolkitHandler != null)
|
||||
&& !uiToolkitHandler.isInReadyState(fInfo.object)) {
|
||||
continue;
|
||||
}
|
||||
// System.err.println("Timeline @" + Timeline.this.hashCode()
|
||||
// + " at position " + timelinePosition);
|
||||
fInfo.updateFieldValue(timelinePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RunOnUIThread
|
||||
private class UISetter extends Setter {
|
||||
}
|
||||
|
||||
class Chain implements TimelineCallback {
|
||||
private List<TimelineCallback> callbacks;
|
||||
|
||||
public Chain(TimelineCallback... callbacks) {
|
||||
this.callbacks = new ArrayList<TimelineCallback>();
|
||||
for (TimelineCallback callback : callbacks) {
|
||||
this.callbacks.add(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public void addCallback(TimelineCallback callback) {
|
||||
this.callbacks.add(callback);
|
||||
}
|
||||
|
||||
public void removeCallback(TimelineCallback callback) {
|
||||
this.callbacks.remove(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineStateChanged(final TimelineState oldState,
|
||||
final TimelineState newState, final float durationFraction,
|
||||
final float timelinePosition) {
|
||||
if ((uiToolkitHandler != null)
|
||||
&& !uiToolkitHandler.isInReadyState(mainObject)) {
|
||||
return;
|
||||
}
|
||||
for (int i = this.callbacks.size() - 1; i >= 0; i--) {
|
||||
final TimelineCallback callback = this.callbacks.get(i);
|
||||
// special handling for chained callbacks not running on UI
|
||||
// thread
|
||||
boolean shouldRunOnUIThread = false;
|
||||
Class<?> clazz = callback.getClass();
|
||||
while ((clazz != null) && !shouldRunOnUIThread) {
|
||||
shouldRunOnUIThread = clazz
|
||||
.isAnnotationPresent(RunOnUIThread.class);
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
if (shouldRunOnUIThread
|
||||
&& (Timeline.this.uiToolkitHandler != null)) {
|
||||
Timeline.this.uiToolkitHandler.runOnUIThread(mainObject,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callback.onTimelineStateChanged(oldState,
|
||||
newState, durationFraction,
|
||||
timelinePosition);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback.onTimelineStateChanged(oldState, newState,
|
||||
durationFraction, timelinePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelinePulse(final float durationFraction,
|
||||
final float timelinePosition) {
|
||||
if ((uiToolkitHandler != null)
|
||||
&& !uiToolkitHandler.isInReadyState(mainObject)) {
|
||||
return;
|
||||
}
|
||||
for (int i = this.callbacks.size() - 1; i >= 0; i--) {
|
||||
final TimelineCallback callback = this.callbacks.get(i);
|
||||
// special handling for chained callbacks not running on UI
|
||||
// thread
|
||||
boolean shouldRunOnUIThread = false;
|
||||
Class<?> clazz = callback.getClass();
|
||||
while ((clazz != null) && !shouldRunOnUIThread) {
|
||||
shouldRunOnUIThread = clazz
|
||||
.isAnnotationPresent(RunOnUIThread.class);
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
if (shouldRunOnUIThread
|
||||
&& (Timeline.this.uiToolkitHandler != null)) {
|
||||
Timeline.this.uiToolkitHandler.runOnUIThread(mainObject,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (Timeline.this.getState() == TimelineState.CANCELLED) {
|
||||
return;
|
||||
}
|
||||
// System.err.println("Timeline @"
|
||||
// + Timeline.this.hashCode());
|
||||
callback.onTimelinePulse(durationFraction,
|
||||
timelinePosition);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// System.err.println("Timeline @" +
|
||||
// Timeline.this.hashCode());
|
||||
callback.onTimelinePulse(durationFraction, timelinePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Timeline() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public Timeline(Object mainTimelineObject) {
|
||||
this.mainObject = mainTimelineObject;
|
||||
|
||||
for (UIToolkitHandler uiToolkitHandler : TridentConfig.getInstance()
|
||||
.getUIToolkitHandlers()) {
|
||||
if (uiToolkitHandler.isHandlerFor(mainTimelineObject)) {
|
||||
this.uiToolkitHandler = uiToolkitHandler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if the main timeline object is handled by a UI toolkit handler,
|
||||
// the setters registered with the different addProperty
|
||||
// APIs need to run with the matching threading policy
|
||||
TimelineCallback setterCallback = (this.uiToolkitHandler != null) ? new UISetter()
|
||||
: new Setter();
|
||||
this.callback = new Chain(setterCallback);
|
||||
|
||||
this.duration = 500;
|
||||
this.propertiesToInterpolate = new ArrayList<AbstractFieldInfo>();
|
||||
this.id = Timeline.getId();
|
||||
// this.loopsToLive = -1;
|
||||
|
||||
this.stateStack = new Stack<TimelineState>();
|
||||
this.stateStack.push(TimelineState.IDLE);
|
||||
this.doneCount = 0;
|
||||
|
||||
this.ease = new Linear();
|
||||
}
|
||||
|
||||
public final void setSecondaryID(Comparable<?> secondaryId) {
|
||||
if (this.getState() != TimelineState.IDLE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot change state of non-idle timeline ["
|
||||
+ this.toString() + "]");
|
||||
}
|
||||
this.secondaryId = secondaryId;
|
||||
}
|
||||
|
||||
public final void setDuration(long durationMs) {
|
||||
if (this.getState() != TimelineState.IDLE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot change state of non-idle timeline ["
|
||||
+ this.toString() + "]");
|
||||
}
|
||||
this.duration = durationMs;
|
||||
}
|
||||
|
||||
public final void setInitialDelay(long initialDelay) {
|
||||
if (this.getState() != TimelineState.IDLE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot change state of non-idle timeline ["
|
||||
+ this.toString() + "]");
|
||||
}
|
||||
this.initialDelay = initialDelay;
|
||||
}
|
||||
|
||||
public final void setCycleDelay(long cycleDelay) {
|
||||
if (this.getState() != TimelineState.IDLE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot change state of non-idle timeline ["
|
||||
+ this.toString() + "]");
|
||||
}
|
||||
this.cycleDelay = cycleDelay;
|
||||
}
|
||||
|
||||
public final void addCallback(TimelineCallback callback) {
|
||||
if (this.getState() != TimelineState.IDLE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot change state of non-idle timeline ["
|
||||
+ this.toString() + "]");
|
||||
}
|
||||
this.callback.addCallback(callback);
|
||||
}
|
||||
|
||||
public final void removeCallback(TimelineCallback callback) {
|
||||
if (this.getState() != TimelineState.IDLE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot change state of non-idle timeline ["
|
||||
+ this.toString() + "]");
|
||||
}
|
||||
this.callback.removeCallback(callback);
|
||||
}
|
||||
|
||||
public static <T> TimelinePropertyBuilder<T> property(String propertyName) {
|
||||
return new TimelinePropertyBuilder<T>(propertyName);
|
||||
}
|
||||
|
||||
public final <T> void addPropertyToInterpolate(
|
||||
TimelinePropertyBuilder<T> propertyBuilder) {
|
||||
this.propertiesToInterpolate.add(propertyBuilder.getFieldInfo(this));
|
||||
}
|
||||
|
||||
public final <T> void addPropertyToInterpolate(String propName,
|
||||
KeyFrames<T> keyFrames) {
|
||||
this.addPropertyToInterpolate(Timeline.<T> property(propName)
|
||||
.goingThrough(keyFrames));
|
||||
}
|
||||
|
||||
public final <T> void addPropertyToInterpolate(String propName, T from, T to) {
|
||||
this.addPropertyToInterpolate(Timeline.<T> property(propName)
|
||||
.from(from).to(to));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void play() {
|
||||
this.playSkipping(0);
|
||||
}
|
||||
|
||||
public void playSkipping(final long msToSkip) {
|
||||
if ((this.initialDelay + this.duration) < msToSkip) {
|
||||
throw new IllegalArgumentException(
|
||||
"Required skip longer than initial delay + duration");
|
||||
}
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.PLAY, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Timeline.this.isLooping = false;
|
||||
TimelineEngine.getInstance().play(Timeline.this, false,
|
||||
msToSkip);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void playReverse() {
|
||||
playReverseSkipping(0);
|
||||
}
|
||||
|
||||
public void playReverseSkipping(final long msToSkip) {
|
||||
if ((this.initialDelay + this.duration) < msToSkip) {
|
||||
throw new IllegalArgumentException(
|
||||
"Required skip longer than initial delay + duration");
|
||||
}
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.PLAY, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Timeline.this.isLooping = false;
|
||||
TimelineEngine.getInstance().playReverse(Timeline.this,
|
||||
false, msToSkip);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void replay() {
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.PLAY, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Timeline.this.isLooping = false;
|
||||
TimelineEngine.getInstance().play(Timeline.this, true,
|
||||
0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void replayReverse() {
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.PLAY, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Timeline.this.isLooping = false;
|
||||
TimelineEngine.getInstance().playReverse(Timeline.this,
|
||||
true, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void playLoop(RepeatBehavior repeatBehavior) {
|
||||
this.playLoop(-1, repeatBehavior);
|
||||
}
|
||||
|
||||
public void playLoopSkipping(RepeatBehavior repeatBehavior,
|
||||
final long msToSkip) {
|
||||
this.playLoopSkipping(-1, repeatBehavior, msToSkip);
|
||||
}
|
||||
|
||||
public void playLoop(int loopCount, RepeatBehavior repeatBehavior) {
|
||||
this.playLoopSkipping(loopCount, repeatBehavior, 0);
|
||||
}
|
||||
|
||||
public void playLoopSkipping(final int loopCount,
|
||||
final RepeatBehavior repeatBehavior, final long msToSkip) {
|
||||
if ((this.initialDelay + this.duration) < msToSkip) {
|
||||
throw new IllegalArgumentException(
|
||||
"Required skip longer than initial delay + duration");
|
||||
}
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.PLAY, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Timeline.this.isLooping = true;
|
||||
Timeline.this.repeatCount = loopCount;
|
||||
Timeline.this.repeatBehavior = repeatBehavior;
|
||||
TimelineEngine.getInstance().playLoop(Timeline.this,
|
||||
msToSkip);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels this timeline. The timeline transitions to the
|
||||
* {@link TimelineState#CANCELLED} state, preserving its current timeline
|
||||
* position. After application callbacks and field interpolations are done
|
||||
* on the {@link TimelineState#CANCELLED} state, the timeline transitions to
|
||||
* the {@link TimelineState#IDLE} state. Application callbacks and field
|
||||
* interpolations are done on this state as well.
|
||||
*
|
||||
* @see #end()
|
||||
* @see #abort()
|
||||
*/
|
||||
public void cancel() {
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.CANCEL, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends this timeline. The timeline transitions to the
|
||||
* {@link TimelineState#DONE} state, with the timeline position set to 0.0
|
||||
* or 1.0 - based on the direction of the timeline. After application
|
||||
* callbacks and field interpolations are done on the
|
||||
* {@link TimelineState#DONE} state, the timeline transitions to the
|
||||
* {@link TimelineState#IDLE} state. Application callbacks and field
|
||||
* interpolations are done on this state as well.
|
||||
*
|
||||
* @see #cancel()
|
||||
* @see #abort()
|
||||
*/
|
||||
public void end() {
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.END, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts this timeline. The timeline transitions to the
|
||||
* {@link TimelineState#IDLE} state. No application callbacks or field
|
||||
* interpolations are done.
|
||||
*
|
||||
* @see #cancel()
|
||||
* @see #end()
|
||||
*/
|
||||
public void abort() {
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.ABORT, null);
|
||||
}
|
||||
|
||||
public void suspend() {
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.SUSPEND, null);
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
TimelineEngine.getInstance().runTimelineOperation(this,
|
||||
TimelineOperationKind.RESUME, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified timeline should stop at the end of the cycle.
|
||||
* This method should be called only on looping timelines.
|
||||
*/
|
||||
public void cancelAtCycleBreak() {
|
||||
if (!this.isLooping) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can only be called on looping timelines");
|
||||
}
|
||||
this.toCancelAtCycleBreak = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique ID.
|
||||
*
|
||||
* @return Unique ID.
|
||||
*/
|
||||
protected static synchronized long getId() {
|
||||
return counter++;
|
||||
}
|
||||
|
||||
public final float getTimelinePosition() {
|
||||
return this.timelinePosition;
|
||||
}
|
||||
|
||||
public final float getDurationFraction() {
|
||||
return this.durationFraction;
|
||||
}
|
||||
|
||||
public final TimelineState getState() {
|
||||
return this.stateStack.peek();
|
||||
}
|
||||
|
||||
public final void setEase(TimelineEase ease) {
|
||||
if (this.getState() != TimelineState.IDLE) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot change state of non-idle timeline");
|
||||
}
|
||||
this.ease = ease;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return (this.doneCount > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsReplay() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetDoneFlag() {
|
||||
this.doneCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer res = new StringBuffer();
|
||||
if (this.name != null) {
|
||||
res.append(this.name);
|
||||
}
|
||||
if (this.mainObject != null) {
|
||||
res.append(":" + this.mainObject.getClass().getName());
|
||||
}
|
||||
if (this.secondaryId != null) {
|
||||
res.append(":" + this.secondaryId.toString());
|
||||
}
|
||||
|
||||
res.append(" " + this.getState().name());
|
||||
res.append(":" + this.timelinePosition);
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
void replaceState(TimelineState state) {
|
||||
this.stateStack.pop();
|
||||
this.pushState(state);
|
||||
}
|
||||
|
||||
void pushState(TimelineState state) {
|
||||
if (state == TimelineState.DONE) {
|
||||
this.doneCount++;
|
||||
}
|
||||
this.stateStack.add(state);
|
||||
}
|
||||
|
||||
TimelineState popState() {
|
||||
return this.stateStack.pop();
|
||||
}
|
||||
|
||||
public final long getDuration() {
|
||||
return this.duration;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Object getMainObject() {
|
||||
return this.mainObject;
|
||||
}
|
||||
|
||||
// permit to dynamically change the from and to colors of a property of the transition
|
||||
public <T> void setPropertyValues(int index, T from, T to) {
|
||||
propertiesToInterpolate.get(index).setValues(from, to);
|
||||
}
|
||||
}
|
||||
+959
@@ -0,0 +1,959 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import org.pushingpixels.trident.Timeline.TimelineState;
|
||||
import org.pushingpixels.trident.TimelineScenario.TimelineScenarioState;
|
||||
import org.pushingpixels.trident.callback.RunOnUIThread;
|
||||
|
||||
/**
|
||||
* The Trident timeline engine. This is the main entry point to play
|
||||
* {@link Timeline}s and {@link TimelineScenario}s. Use the
|
||||
* {@link #getInstance()} method to get the timeline engine.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes","unchecked","incomplete-switch"})
|
||||
class TimelineEngine {
|
||||
/**
|
||||
* Debug mode indicator. Set to <code>true</code> to have trace messages on
|
||||
* console.
|
||||
*/
|
||||
public static boolean DEBUG_MODE = false;
|
||||
|
||||
/**
|
||||
* Single instance of <code>this</code> class.
|
||||
*/
|
||||
private static TimelineEngine instance;
|
||||
|
||||
/**
|
||||
* All currently running timelines.
|
||||
*/
|
||||
private Set<Timeline> runningTimelines;
|
||||
|
||||
enum TimelineOperationKind {
|
||||
PLAY, CANCEL, RESUME, SUSPEND, ABORT, END
|
||||
}
|
||||
|
||||
class TimelineOperation {
|
||||
public TimelineOperationKind operationKind;
|
||||
|
||||
Runnable operationRunnable;
|
||||
|
||||
public TimelineOperation(TimelineOperationKind operationKind,
|
||||
Runnable operationRunnable) {
|
||||
this.operationKind = operationKind;
|
||||
this.operationRunnable = operationRunnable;
|
||||
}
|
||||
}
|
||||
|
||||
private Set<TimelineScenario> runningScenarios;
|
||||
|
||||
long lastIterationTimeStamp;
|
||||
|
||||
/**
|
||||
* Identifies a main object and an optional secondary ID.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
static class FullObjectID {
|
||||
/**
|
||||
* Main object for the timeline.
|
||||
*/
|
||||
public Object mainObj;
|
||||
|
||||
/**
|
||||
* ID to distinguish between different sub-components of
|
||||
* {@link #mainObj}. For example, the tabbed pane uses this field to
|
||||
* make tab-specific animations.
|
||||
*/
|
||||
public Comparable subID;
|
||||
|
||||
/**
|
||||
* Creates a new object ID.
|
||||
*
|
||||
* @param mainObj
|
||||
* The main object.
|
||||
* @param subID
|
||||
* ID to distinguish between different sub-components of
|
||||
* <code>mainObj</code>. Can be <code>null</code>.
|
||||
*/
|
||||
public FullObjectID(Object mainObj, Comparable subID) {
|
||||
this.mainObj = mainObj;
|
||||
this.subID = subID;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.mainObj.hashCode();
|
||||
if (this.subID != null)
|
||||
result &= (this.subID.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof FullObjectID) {
|
||||
FullObjectID cid = (FullObjectID) obj;
|
||||
try {
|
||||
boolean result = (this.mainObj == cid.mainObj);
|
||||
if (this.subID == null) {
|
||||
result = result && (cid.subID == null);
|
||||
} else {
|
||||
result = result
|
||||
&& (this.subID.compareTo(cid.subID) == 0);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception exc) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.mainObj.getClass().getSimpleName() + ":" + this.subID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The timeline thread.
|
||||
*/
|
||||
TridentAnimationThread animatorThread;
|
||||
|
||||
private BlockingQueue<Runnable> callbackQueue;
|
||||
|
||||
private TimelineCallbackThread callbackThread;
|
||||
|
||||
class TridentAnimationThread extends Thread {
|
||||
public TridentAnimationThread() {
|
||||
super();
|
||||
this.setName("Trident pulse source thread");
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Thread#run()
|
||||
*/
|
||||
@Override
|
||||
public final void run() {
|
||||
TridentConfig.PulseSource pulseSource = TridentConfig.getInstance()
|
||||
.getPulseSource();
|
||||
lastIterationTimeStamp = System.currentTimeMillis();
|
||||
while (true) {
|
||||
pulseSource.waitUntilNextPulse();
|
||||
updateTimelines();
|
||||
// engine.currLoopId++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interrupt() {
|
||||
System.err.println("Interrupted");
|
||||
super.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private class TimelineCallbackThread extends Thread {
|
||||
public TimelineCallbackThread() {
|
||||
super();
|
||||
this.setName("Trident callback thread");
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Runnable runnable = callbackQueue.take();
|
||||
runnable.run();
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple constructor. Defined private for singleton.
|
||||
*
|
||||
* @see #getInstance()
|
||||
*/
|
||||
private TimelineEngine() {
|
||||
this.runningTimelines = new HashSet<Timeline>();
|
||||
this.runningScenarios = new HashSet<TimelineScenario>();
|
||||
|
||||
this.callbackQueue = new LinkedBlockingQueue<Runnable>();
|
||||
this.callbackThread = this.getCallbackThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets singleton instance.
|
||||
*
|
||||
* @return Singleton instance.
|
||||
*/
|
||||
public synchronized static TimelineEngine getInstance() {
|
||||
if (TimelineEngine.instance == null) {
|
||||
TimelineEngine.instance = new TimelineEngine();
|
||||
}
|
||||
return TimelineEngine.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all timelines that are currently registered with
|
||||
* <code>this</code> tracker.
|
||||
*/
|
||||
void updateTimelines() {
|
||||
synchronized (LOCK) {
|
||||
if ((this.runningTimelines.size() == 0)
|
||||
&& (this.runningScenarios.size() == 0)) {
|
||||
this.lastIterationTimeStamp = System.currentTimeMillis();
|
||||
return;
|
||||
}
|
||||
|
||||
long passedSinceLastIteration = (System.currentTimeMillis() - this.lastIterationTimeStamp);
|
||||
if (passedSinceLastIteration < 0) {
|
||||
// ???
|
||||
passedSinceLastIteration = 0;
|
||||
}
|
||||
if (DEBUG_MODE) {
|
||||
System.out.println("Elapsed since last iteration: "
|
||||
+ passedSinceLastIteration + "ms");
|
||||
}
|
||||
|
||||
// System.err.println("Periodic update on "
|
||||
// + this.runningTimelines.size() + " timelines; "
|
||||
// + passedSinceLastIteration + " ms passed since last");
|
||||
// for (Timeline t : runningTimelines) {
|
||||
// if (t.mainObject != null
|
||||
// && t.mainObject.getClass().getName().indexOf(
|
||||
// "ProgressBar") >= 0) {
|
||||
// continue;
|
||||
// }
|
||||
// System.err.println("\tTimeline @"
|
||||
// + t.hashCode()
|
||||
// + " ["
|
||||
// + t.getName()
|
||||
// + "] on "
|
||||
// + (t.mainObject == null ? "null" : t.mainObject
|
||||
// .getClass().getName()));
|
||||
// }
|
||||
for (Iterator<Timeline> itTimeline = this.runningTimelines
|
||||
.iterator(); itTimeline.hasNext();) {
|
||||
Timeline timeline = itTimeline.next();
|
||||
if (timeline.getState() == TimelineState.SUSPENDED)
|
||||
continue;
|
||||
|
||||
boolean timelineWasInReadyState = false;
|
||||
if (timeline.getState() == TimelineState.READY) {
|
||||
if ((timeline.timeUntilPlay - passedSinceLastIteration) > 0) {
|
||||
// still needs to wait in the READY state
|
||||
timeline.timeUntilPlay -= passedSinceLastIteration;
|
||||
continue;
|
||||
}
|
||||
|
||||
// can go from READY to PLAYING
|
||||
timelineWasInReadyState = true;
|
||||
timeline.popState();
|
||||
this.callbackCallTimelineStateChanged(timeline,
|
||||
TimelineState.READY);
|
||||
}
|
||||
|
||||
boolean hasEnded = false;
|
||||
if (DEBUG_MODE) {
|
||||
System.out.println("Processing " + timeline.id + "["
|
||||
+ timeline.mainObject.getClass().getSimpleName()
|
||||
+ "] from " + timeline.durationFraction
|
||||
+ ". Callback - "
|
||||
+ (timeline.callback == null ? "no" : "yes"));
|
||||
}
|
||||
// Component comp = entry.getKey();
|
||||
|
||||
// at this point, the timeline must be playing
|
||||
switch (timeline.getState()) {
|
||||
case PLAYING_FORWARD:
|
||||
if (!timelineWasInReadyState) {
|
||||
timeline.durationFraction = timeline.durationFraction
|
||||
+ (float) passedSinceLastIteration
|
||||
/ (float) timeline.duration;
|
||||
}
|
||||
timeline.timelinePosition = timeline.ease
|
||||
.map(timeline.durationFraction);
|
||||
if (DEBUG_MODE) {
|
||||
System.out
|
||||
.println("Timeline position: "
|
||||
+ ((long) (timeline.durationFraction * timeline.duration))
|
||||
+ "/" + timeline.duration + " = "
|
||||
+ timeline.durationFraction);
|
||||
}
|
||||
if (timeline.durationFraction > 1.0f) {
|
||||
timeline.durationFraction = 1.0f;
|
||||
timeline.timelinePosition = 1.0f;
|
||||
if (timeline.isLooping) {
|
||||
boolean stopLoopingAnimation = timeline.toCancelAtCycleBreak;
|
||||
int loopsToLive = timeline.repeatCount;
|
||||
if (loopsToLive > 0) {
|
||||
loopsToLive--;
|
||||
stopLoopingAnimation = stopLoopingAnimation
|
||||
|| (loopsToLive == 0);
|
||||
timeline.repeatCount = loopsToLive;
|
||||
}
|
||||
if (stopLoopingAnimation) {
|
||||
// end looping animation
|
||||
hasEnded = true;
|
||||
itTimeline.remove();
|
||||
} else {
|
||||
if (timeline.repeatBehavior == Timeline.RepeatBehavior.REVERSE) {
|
||||
timeline
|
||||
.replaceState(TimelineState.PLAYING_REVERSE);
|
||||
if (timeline.cycleDelay > 0) {
|
||||
timeline.pushState(TimelineState.READY);
|
||||
timeline.timeUntilPlay = timeline.cycleDelay;
|
||||
}
|
||||
this.callbackCallTimelineStateChanged(
|
||||
timeline,
|
||||
TimelineState.PLAYING_FORWARD);
|
||||
} else {
|
||||
timeline.durationFraction = 0.0f;
|
||||
timeline.timelinePosition = 0.0f;
|
||||
if (timeline.cycleDelay > 0) {
|
||||
timeline.pushState(TimelineState.READY);
|
||||
timeline.timeUntilPlay = timeline.cycleDelay;
|
||||
this.callbackCallTimelineStateChanged(
|
||||
timeline,
|
||||
TimelineState.PLAYING_FORWARD);
|
||||
} else {
|
||||
// it's still playing forward, but lets
|
||||
// the app code know
|
||||
// that the new loop has begun
|
||||
this.callbackCallTimelineStateChanged(
|
||||
timeline,
|
||||
TimelineState.PLAYING_FORWARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hasEnded = true;
|
||||
itTimeline.remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PLAYING_REVERSE:
|
||||
if (!timelineWasInReadyState) {
|
||||
timeline.durationFraction = timeline.durationFraction
|
||||
- (float) passedSinceLastIteration
|
||||
/ (float) timeline.duration;
|
||||
}
|
||||
timeline.timelinePosition = timeline.ease
|
||||
.map(timeline.durationFraction);
|
||||
// state.timelinePosition = state.timelinePosition
|
||||
// - stepFactor
|
||||
// * state.fadeStep.getNextStep(state.timelineKind,
|
||||
// state.timelinePosition,
|
||||
// state.isPlayingForward, state.isLooping);
|
||||
if (DEBUG_MODE) {
|
||||
System.out
|
||||
.println("Timeline position: "
|
||||
+ ((long) (timeline.durationFraction * timeline.duration))
|
||||
+ "/" + timeline.duration + " = "
|
||||
+ timeline.durationFraction);
|
||||
}
|
||||
if (timeline.durationFraction < 0) {
|
||||
timeline.durationFraction = 0.0f;
|
||||
timeline.timelinePosition = 0.0f;
|
||||
if (timeline.isLooping) {
|
||||
boolean stopLoopingAnimation = timeline.toCancelAtCycleBreak;
|
||||
int loopsToLive = timeline.repeatCount;
|
||||
if (loopsToLive > 0) {
|
||||
loopsToLive--;
|
||||
stopLoopingAnimation = stopLoopingAnimation
|
||||
|| (loopsToLive == 0);
|
||||
timeline.repeatCount = loopsToLive;
|
||||
}
|
||||
if (stopLoopingAnimation) {
|
||||
// end looping animation
|
||||
hasEnded = true;
|
||||
itTimeline.remove();
|
||||
} else {
|
||||
timeline
|
||||
.replaceState(TimelineState.PLAYING_FORWARD);
|
||||
if (timeline.cycleDelay > 0) {
|
||||
timeline.pushState(TimelineState.READY);
|
||||
timeline.timeUntilPlay = timeline.cycleDelay;
|
||||
}
|
||||
this.callbackCallTimelineStateChanged(timeline,
|
||||
TimelineState.PLAYING_REVERSE);
|
||||
}
|
||||
} else {
|
||||
hasEnded = true;
|
||||
itTimeline.remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Timeline cannot be in "
|
||||
+ timeline.getState() + " state");
|
||||
}
|
||||
if (hasEnded) {
|
||||
if (DEBUG_MODE) {
|
||||
System.out.println("Ending " + timeline.id + " on "
|
||||
// + timeline.timelineKind.toString()
|
||||
+ " in state " + timeline.getState().name()
|
||||
+ " at position " + timeline.durationFraction);
|
||||
}
|
||||
TimelineState oldState = timeline.getState();
|
||||
timeline.replaceState(TimelineState.DONE);
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
timeline.popState();
|
||||
if (timeline.getState() != TimelineState.IDLE) {
|
||||
throw new IllegalStateException(
|
||||
"Timeline should be IDLE at this point");
|
||||
}
|
||||
this.callbackCallTimelineStateChanged(timeline,
|
||||
TimelineState.DONE);
|
||||
} else {
|
||||
if (DEBUG_MODE) {
|
||||
System.out.println("Calling " + timeline.id + " on "
|
||||
// + timeline.timelineKind.toString() + " at "
|
||||
+ timeline.durationFraction);
|
||||
}
|
||||
this.callbackCallTimelinePulse(timeline);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.runningScenarios.size() > 0) {
|
||||
// System.err.println(Thread.currentThread().getName()
|
||||
// + " : updating");
|
||||
for (Iterator<TimelineScenario> it = this.runningScenarios
|
||||
.iterator(); it.hasNext();) {
|
||||
TimelineScenario scenario = it.next();
|
||||
if (scenario.state == TimelineScenarioState.DONE) {
|
||||
it.remove();
|
||||
this.callbackCallTimelineScenarioEnded(scenario);
|
||||
continue;
|
||||
}
|
||||
Set<TimelineScenario.TimelineScenarioActor> readyActors = scenario
|
||||
.getReadyActors();
|
||||
if (readyActors != null) {
|
||||
// if (readyActors.size() > 0)
|
||||
// System.out.println("Scenario : " + scenario.state +
|
||||
// ":"
|
||||
// + readyActors.size());
|
||||
for (TimelineScenario.TimelineScenarioActor readyActor : readyActors) {
|
||||
readyActor.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// System.err.println("Periodic update done");
|
||||
|
||||
// this.nothingTracked = (this.runningTimelines.size() == 0);
|
||||
this.lastIterationTimeStamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
private void callbackCallTimelineStateChanged(final Timeline timeline,
|
||||
final TimelineState oldState) {
|
||||
final TimelineState newState = timeline.getState();
|
||||
final float durationFraction = timeline.durationFraction;
|
||||
final float timelinePosition = timeline.timelinePosition;
|
||||
Runnable callbackRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean shouldRunOnUIThread = false;
|
||||
Class<?> clazz = timeline.callback.getClass();
|
||||
while ((clazz != null) && !shouldRunOnUIThread) {
|
||||
shouldRunOnUIThread = clazz
|
||||
.isAnnotationPresent(RunOnUIThread.class);
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
if (shouldRunOnUIThread && (timeline.uiToolkitHandler != null)) {
|
||||
timeline.uiToolkitHandler.runOnUIThread(
|
||||
timeline.mainObject, new Runnable() {
|
||||
public void run() {
|
||||
timeline.callback.onTimelineStateChanged(
|
||||
oldState, newState,
|
||||
durationFraction, timelinePosition);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
timeline.callback.onTimelineStateChanged(oldState,
|
||||
newState, durationFraction, timelinePosition);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.callbackQueue.add(callbackRunnable);
|
||||
}
|
||||
|
||||
private void callbackCallTimelinePulse(final Timeline timeline) {
|
||||
final float durationFraction = timeline.durationFraction;
|
||||
final float timelinePosition = timeline.timelinePosition;
|
||||
Runnable callbackRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean shouldRunOnUIThread = false;
|
||||
Class<?> clazz = timeline.callback.getClass();
|
||||
while ((clazz != null) && !shouldRunOnUIThread) {
|
||||
shouldRunOnUIThread = clazz
|
||||
.isAnnotationPresent(RunOnUIThread.class);
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
if (shouldRunOnUIThread && (timeline.uiToolkitHandler != null)) {
|
||||
timeline.uiToolkitHandler.runOnUIThread(
|
||||
timeline.mainObject, new Runnable() {
|
||||
public void run() {
|
||||
// System.err.println("Timeline @"
|
||||
// + timeline.hashCode());
|
||||
timeline.callback.onTimelinePulse(
|
||||
durationFraction, timelinePosition);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// System.err.println("Timeline @" + timeline.hashCode());
|
||||
timeline.callback.onTimelinePulse(durationFraction,
|
||||
timelinePosition);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.callbackQueue.add(callbackRunnable);
|
||||
}
|
||||
|
||||
private void callbackCallTimelineScenarioEnded(
|
||||
final TimelineScenario timelineScenario) {
|
||||
Runnable callbackRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
timelineScenario.callback.onTimelineScenarioDone();
|
||||
}
|
||||
};
|
||||
this.callbackQueue.offer(callbackRunnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an existing running timeline that matches the specified
|
||||
* parameters.
|
||||
*
|
||||
* @param timelineKind
|
||||
* Timeline kind.
|
||||
* @param object
|
||||
* Component.
|
||||
* @param secondaryId
|
||||
* Secondary id. Relevant for such components as tabbed panes
|
||||
* (where animation is performed on different tabs).
|
||||
* @return An existing running timeline that matches the specified
|
||||
* parameters.
|
||||
*/
|
||||
private Timeline getRunningTimeline(Timeline timeline) {
|
||||
synchronized (LOCK) {
|
||||
if (this.runningTimelines.contains(timeline))
|
||||
return timeline;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified timeline.
|
||||
*
|
||||
* @param timeline
|
||||
* Timeline to add.
|
||||
*/
|
||||
private void addTimeline(Timeline timeline) {
|
||||
synchronized (LOCK) {
|
||||
FullObjectID cid = new FullObjectID(timeline.mainObject,
|
||||
timeline.secondaryId);
|
||||
timeline.fullObjectID = cid;
|
||||
this.runningTimelines.add(timeline);
|
||||
// this.nothingTracked = false;
|
||||
if (DEBUG_MODE) {
|
||||
System.out.println("Added (" + timeline.id + ") on "
|
||||
+ timeline.fullObjectID + "]. Fade "
|
||||
// + timeline.timelineKind.toString() + " with state "
|
||||
+ timeline.getState().name() + ". Callback - "
|
||||
+ (timeline.callback == null ? "no" : "yes"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void play(Timeline timeline, boolean reset, long msToSkip) {
|
||||
synchronized (LOCK) {
|
||||
getAnimatorThread();
|
||||
|
||||
// see if it's already tracked
|
||||
Timeline existing = this.getRunningTimeline(timeline);
|
||||
if (existing == null) {
|
||||
TimelineState oldState = timeline.getState();
|
||||
timeline.timeUntilPlay = timeline.initialDelay - msToSkip;
|
||||
if (timeline.timeUntilPlay < 0) {
|
||||
timeline.durationFraction = (float) -timeline.timeUntilPlay
|
||||
/ (float) timeline.duration;
|
||||
timeline.timelinePosition = timeline.ease
|
||||
.map(timeline.durationFraction);
|
||||
timeline.timeUntilPlay = 0;
|
||||
} else {
|
||||
timeline.durationFraction = 0.0f;
|
||||
timeline.timelinePosition = 0.0f;
|
||||
}
|
||||
timeline.pushState(TimelineState.PLAYING_FORWARD);
|
||||
timeline.pushState(TimelineState.READY);
|
||||
this.addTimeline(timeline);
|
||||
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
} else {
|
||||
TimelineState oldState = existing.getState();
|
||||
if (oldState == TimelineState.READY) {
|
||||
// the timeline remains READY, but after that it will be
|
||||
// PLAYING_FORWARD
|
||||
existing.popState();
|
||||
existing.replaceState(TimelineState.PLAYING_FORWARD);
|
||||
existing.pushState(TimelineState.READY);
|
||||
} else {
|
||||
// change the timeline state
|
||||
existing.replaceState(TimelineState.PLAYING_FORWARD);
|
||||
if (oldState != existing.getState()) {
|
||||
this.callbackCallTimelineStateChanged(timeline,
|
||||
oldState);
|
||||
}
|
||||
}
|
||||
if (reset) {
|
||||
existing.durationFraction = 0.0f;
|
||||
existing.timelinePosition = 0.0f;
|
||||
this.callbackCallTimelinePulse(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void playScenario(TimelineScenario scenario) {
|
||||
synchronized (LOCK) {
|
||||
getAnimatorThread();
|
||||
Set<TimelineScenario.TimelineScenarioActor> readyActors = scenario
|
||||
.getReadyActors();
|
||||
|
||||
// System.err.println(Thread.currentThread().getName() +
|
||||
// " : adding");
|
||||
this.runningScenarios.add(scenario);
|
||||
for (TimelineScenario.TimelineScenarioActor readyActor : readyActors) {
|
||||
readyActor.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void playReverse(Timeline timeline, boolean reset, long msToSkip) {
|
||||
synchronized (LOCK) {
|
||||
getAnimatorThread();
|
||||
if (timeline.isLooping) {
|
||||
throw new IllegalArgumentException(
|
||||
"Timeline must not be marked as looping");
|
||||
}
|
||||
|
||||
// see if it's already tracked
|
||||
Timeline existing = this.getRunningTimeline(timeline);
|
||||
if (existing == null) {
|
||||
TimelineState oldState = timeline.getState();
|
||||
timeline.timeUntilPlay = timeline.initialDelay - msToSkip;
|
||||
if (timeline.timeUntilPlay < 0) {
|
||||
timeline.durationFraction = 1.0f
|
||||
- (float) -timeline.timeUntilPlay
|
||||
/ (float) timeline.duration;
|
||||
timeline.timelinePosition = timeline.ease
|
||||
.map(timeline.durationFraction);
|
||||
timeline.timeUntilPlay = 0;
|
||||
} else {
|
||||
timeline.durationFraction = 1.0f;
|
||||
timeline.timelinePosition = 1.0f;
|
||||
}
|
||||
timeline.pushState(TimelineState.PLAYING_REVERSE);
|
||||
timeline.pushState(TimelineState.READY);
|
||||
|
||||
this.addTimeline(timeline);
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
} else {
|
||||
TimelineState oldState = existing.getState();
|
||||
if (oldState == TimelineState.READY) {
|
||||
// the timeline remains READY, but after that it will be
|
||||
// PLAYING_REVERSE
|
||||
existing.popState();
|
||||
existing.replaceState(TimelineState.PLAYING_REVERSE);
|
||||
existing.pushState(TimelineState.READY);
|
||||
} else {
|
||||
// change the timeline state
|
||||
existing.replaceState(TimelineState.PLAYING_REVERSE);
|
||||
if (oldState != existing.getState()) {
|
||||
this.callbackCallTimelineStateChanged(timeline,
|
||||
oldState);
|
||||
}
|
||||
}
|
||||
if (reset) {
|
||||
existing.durationFraction = 1.0f;
|
||||
existing.timelinePosition = 1.0f;
|
||||
this.callbackCallTimelinePulse(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void playLoop(Timeline timeline, long msToSkip) {
|
||||
synchronized (LOCK) {
|
||||
getAnimatorThread();
|
||||
if (!timeline.isLooping) {
|
||||
throw new IllegalArgumentException(
|
||||
"Timeline must be marked as looping");
|
||||
}
|
||||
|
||||
// see if it's already tracked
|
||||
Timeline existing = this.getRunningTimeline(timeline);
|
||||
if (existing == null) {
|
||||
TimelineState oldState = timeline.getState();
|
||||
timeline.timeUntilPlay = timeline.initialDelay - msToSkip;
|
||||
if (timeline.timeUntilPlay < 0) {
|
||||
timeline.durationFraction = (float) -timeline.timeUntilPlay
|
||||
/ (float) timeline.duration;
|
||||
timeline.timelinePosition = timeline.ease
|
||||
.map(timeline.durationFraction);
|
||||
timeline.timeUntilPlay = 0;
|
||||
} else {
|
||||
timeline.durationFraction = 0.0f;
|
||||
timeline.timelinePosition = 0.0f;
|
||||
}
|
||||
timeline.pushState(TimelineState.PLAYING_FORWARD);
|
||||
timeline.pushState(TimelineState.READY);
|
||||
timeline.toCancelAtCycleBreak = false;
|
||||
|
||||
this.addTimeline(timeline);
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
} else {
|
||||
existing.toCancelAtCycleBreak = false;
|
||||
existing.repeatCount = timeline.repeatCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops tracking of all timelines. Note that this function <b>does not</b>
|
||||
* stop the timeline engine thread ({@link #animatorThread}) and the
|
||||
* timeline callback thread ({@link #callbackThread}).
|
||||
*/
|
||||
public void cancelAllTimelines() {
|
||||
synchronized (LOCK) {
|
||||
getAnimatorThread();
|
||||
for (Timeline timeline : this.runningTimelines) {
|
||||
TimelineState oldState = timeline.getState();
|
||||
while (timeline.getState() != TimelineState.IDLE)
|
||||
timeline.popState();
|
||||
timeline.pushState(TimelineState.CANCELLED);
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
timeline.popState();
|
||||
this.callbackCallTimelineStateChanged(timeline,
|
||||
TimelineState.CANCELLED);
|
||||
}
|
||||
this.runningTimelines.clear();
|
||||
this.runningScenarios.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the animator thread.
|
||||
*
|
||||
* @return The animator thread.
|
||||
*/
|
||||
private TridentAnimationThread getAnimatorThread() {
|
||||
if (this.animatorThread == null) {
|
||||
this.animatorThread = new TridentAnimationThread();
|
||||
this.animatorThread.start();
|
||||
}
|
||||
return this.animatorThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the callback thread.
|
||||
*
|
||||
* @return The animator thread.
|
||||
*/
|
||||
private TimelineCallbackThread getCallbackThread() {
|
||||
if (this.callbackThread == null) {
|
||||
this.callbackThread = new TimelineCallbackThread();
|
||||
this.callbackThread.start();
|
||||
}
|
||||
return this.callbackThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the specified timeline instance.
|
||||
*
|
||||
* @param timeline
|
||||
* Timeline to cancel.
|
||||
*/
|
||||
private void cancelTimeline(Timeline timeline) {
|
||||
getAnimatorThread();
|
||||
if (this.runningTimelines.contains(timeline)) {
|
||||
this.runningTimelines.remove(timeline);
|
||||
TimelineState oldState = timeline.getState();
|
||||
while (timeline.getState() != TimelineState.IDLE)
|
||||
timeline.popState();
|
||||
timeline.pushState(TimelineState.CANCELLED);
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
timeline.popState();
|
||||
this.callbackCallTimelineStateChanged(timeline,
|
||||
TimelineState.CANCELLED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the specified timeline instance.
|
||||
*
|
||||
* @param timeline
|
||||
* Timeline to end.
|
||||
*/
|
||||
private void endTimeline(Timeline timeline) {
|
||||
getAnimatorThread();
|
||||
if (this.runningTimelines.contains(timeline)) {
|
||||
this.runningTimelines.remove(timeline);
|
||||
TimelineState oldState = timeline.getState();
|
||||
float endPosition = timeline.timelinePosition;
|
||||
while (timeline.getState() != TimelineState.IDLE) {
|
||||
TimelineState state = timeline.popState();
|
||||
if (state == TimelineState.PLAYING_FORWARD)
|
||||
endPosition = 1.0f;
|
||||
if (state == TimelineState.PLAYING_REVERSE)
|
||||
endPosition = 0.0f;
|
||||
}
|
||||
timeline.durationFraction = endPosition;
|
||||
timeline.timelinePosition = endPosition;
|
||||
timeline.pushState(TimelineState.DONE);
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
timeline.popState();
|
||||
this.callbackCallTimelineStateChanged(timeline, TimelineState.DONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the specified timeline instance.
|
||||
*
|
||||
* @param timeline
|
||||
* Timeline to cancel.
|
||||
*/
|
||||
private void abortTimeline(Timeline timeline) {
|
||||
getAnimatorThread();
|
||||
if (this.runningTimelines.contains(timeline)) {
|
||||
this.runningTimelines.remove(timeline);
|
||||
while (timeline.getState() != TimelineState.IDLE)
|
||||
timeline.popState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends the specified timeline instance.
|
||||
*
|
||||
* @param timeline
|
||||
* Timeline to suspend.
|
||||
*/
|
||||
private void suspendTimeline(Timeline timeline) {
|
||||
getAnimatorThread();
|
||||
if (this.runningTimelines.contains(timeline)) {
|
||||
TimelineState oldState = timeline.getState();
|
||||
if ((oldState != TimelineState.PLAYING_FORWARD)
|
||||
&& (oldState != TimelineState.PLAYING_REVERSE)
|
||||
&& (oldState != TimelineState.READY)) {
|
||||
return;
|
||||
}
|
||||
timeline.pushState(TimelineState.SUSPENDED);
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume the specified timeline instance.
|
||||
*
|
||||
* @param timeline
|
||||
* Timeline to resume.
|
||||
*/
|
||||
private void resumeTimeline(Timeline timeline) {
|
||||
getAnimatorThread();
|
||||
if (this.runningTimelines.contains(timeline)) {
|
||||
TimelineState oldState = timeline.getState();
|
||||
if (oldState != TimelineState.SUSPENDED)
|
||||
return;
|
||||
timeline.popState();
|
||||
this.callbackCallTimelineStateChanged(timeline, oldState);
|
||||
}
|
||||
}
|
||||
|
||||
void runTimelineOperation(Timeline timeline,
|
||||
TimelineOperationKind operationKind, Runnable operationRunnable) {
|
||||
synchronized (LOCK) {
|
||||
this.getAnimatorThread();
|
||||
switch (operationKind) {
|
||||
case CANCEL:
|
||||
this.cancelTimeline(timeline);
|
||||
return;
|
||||
case END:
|
||||
this.endTimeline(timeline);
|
||||
return;
|
||||
case RESUME:
|
||||
this.resumeTimeline(timeline);
|
||||
return;
|
||||
case SUSPEND:
|
||||
this.suspendTimeline(timeline);
|
||||
return;
|
||||
case ABORT:
|
||||
this.abortTimeline(timeline);
|
||||
return;
|
||||
}
|
||||
operationRunnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
void runTimelineScenario(TimelineScenario timelineScenario,
|
||||
Runnable timelineScenarioRunnable) {
|
||||
synchronized (LOCK) {
|
||||
this.getAnimatorThread();
|
||||
timelineScenarioRunnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
static final Object LOCK = new Object();
|
||||
}
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.pushingpixels.trident.interpolator.KeyFrames;
|
||||
import org.pushingpixels.trident.interpolator.PropertyInterpolator;
|
||||
|
||||
@SuppressWarnings({"rawtypes","unchecked"})
|
||||
public class TimelinePropertyBuilder<T> {
|
||||
|
||||
/**
|
||||
* Defines how to access a property.
|
||||
*/
|
||||
public static interface PropertyAccessor<T> extends PropertyGetter<T>,
|
||||
PropertySetter<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Default property setter.
|
||||
*/
|
||||
public static class DefaultPropertySetter<T> implements PropertySetter<T> {
|
||||
private Method setterMethod;
|
||||
|
||||
public DefaultPropertySetter(Object obj, String fieldName) {
|
||||
setterMethod = getSetter(obj, fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object obj, String fieldName, T value) {
|
||||
try {
|
||||
setterMethod.invoke(obj, value);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(
|
||||
"Unable to set the value of the field '" + fieldName
|
||||
+ "'", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default property getter.
|
||||
*/
|
||||
public static class DefaultPropertyGetter<T> implements PropertyGetter<T> {
|
||||
private Method getterMethod;
|
||||
|
||||
public DefaultPropertyGetter(Object obj, String fieldName) {
|
||||
getterMethod = getGetter(obj, fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(Object obj, String fieldName) {
|
||||
try {
|
||||
return (T) getterMethod.invoke(obj);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(
|
||||
"Unable to get the value of the field '" + fieldName
|
||||
+ "'", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object target; // may be null
|
||||
private final String propertyName; // required
|
||||
private T from; // optional
|
||||
private boolean isFromCurrent;
|
||||
private T to; // must be optional because of KeyFrames
|
||||
private PropertyInterpolator<T> interpolator; // optional
|
||||
private PropertyGetter<T> getter; // optional
|
||||
private PropertySetter<T> setter; // optional
|
||||
private KeyFrames<T> keyFrames; // optional
|
||||
|
||||
TimelinePropertyBuilder(String propertyName) {
|
||||
this.propertyName = propertyName;
|
||||
this.isFromCurrent = false;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> from(T startValue) {
|
||||
if (this.from != null) {
|
||||
throw new IllegalArgumentException("from() can only be called once");
|
||||
}
|
||||
if (this.isFromCurrent) {
|
||||
throw new IllegalArgumentException(
|
||||
"from() cannot be called after fromCurrent()");
|
||||
}
|
||||
if (this.keyFrames != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"from() cannot be called after goingThrough()");
|
||||
}
|
||||
this.from = startValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> fromCurrent() {
|
||||
if (this.isFromCurrent) {
|
||||
throw new IllegalArgumentException(
|
||||
"fromCurrent() can only be called once");
|
||||
}
|
||||
if (this.from != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"fromCurrent() cannot be called after from()");
|
||||
}
|
||||
if (this.keyFrames != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"fromCurrent() cannot be called after goingThrough()");
|
||||
}
|
||||
this.isFromCurrent = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> to(T endValue) {
|
||||
if (this.to != null) {
|
||||
throw new IllegalArgumentException("to() can only be called once");
|
||||
}
|
||||
if (this.keyFrames != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"to() cannot be called after goingThrough()");
|
||||
}
|
||||
this.to = endValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> on(Object object) {
|
||||
this.target = object;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> interpolatedWith(
|
||||
PropertyInterpolator<T> pInterpolator) {
|
||||
if (this.interpolator != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"interpolateWith() can only be called once");
|
||||
}
|
||||
this.interpolator = pInterpolator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> setWith(PropertySetter<T> pSetter) {
|
||||
if (this.setter != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"setWith() can only be called once");
|
||||
}
|
||||
this.setter = pSetter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> getWith(PropertyGetter<T> pGetter) {
|
||||
if (this.getter != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"getWith() can only be called once");
|
||||
}
|
||||
this.getter = pGetter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> accessWith(PropertyAccessor<T> pAccessor) {
|
||||
if ((this.setter != null) || (this.getter != null)) {
|
||||
throw new IllegalArgumentException(
|
||||
"accessWith() can only be called once");
|
||||
}
|
||||
this.setter = pAccessor;
|
||||
this.getter = pAccessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TimelinePropertyBuilder<T> goingThrough(KeyFrames<T> keyFrames) {
|
||||
if (this.keyFrames != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"goingThrough() can only be called once");
|
||||
}
|
||||
if (this.isFromCurrent) {
|
||||
throw new IllegalArgumentException(
|
||||
"goingThrough() cannot be called after fromCurrent()");
|
||||
}
|
||||
if (this.from != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"goingThrough() cannot be called after from()");
|
||||
}
|
||||
if (this.to != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"goingThrough() cannot be called after to()");
|
||||
}
|
||||
this.keyFrames = keyFrames;
|
||||
return this;
|
||||
}
|
||||
|
||||
AbstractFieldInfo getFieldInfo(Timeline timeline) {
|
||||
if (this.target == null) {
|
||||
this.target = timeline.mainObject;
|
||||
}
|
||||
|
||||
if (this.keyFrames != null) {
|
||||
return new KeyFramesFieldInfo(this.target, this.propertyName,
|
||||
this.keyFrames, this.setter);
|
||||
}
|
||||
|
||||
if (this.isFromCurrent) {
|
||||
if (this.interpolator == null) {
|
||||
this.interpolator = TridentConfig.getInstance()
|
||||
.getPropertyInterpolator(this.to);
|
||||
|
||||
if (this.interpolator == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"No interpolator found for "
|
||||
+ this.to.getClass().getName());
|
||||
}
|
||||
}
|
||||
return new GenericFieldInfoTo(this.target, this.propertyName,
|
||||
this.to, this.interpolator, this.getter, this.setter);
|
||||
}
|
||||
|
||||
if (this.interpolator == null) {
|
||||
this.interpolator = TridentConfig.getInstance()
|
||||
.getPropertyInterpolator(this.from, this.to);
|
||||
|
||||
// if (this.interpolator == null) {
|
||||
// throw new IllegalArgumentException("No interpolator found for "
|
||||
// + this.from.getClass().getName() + ":"
|
||||
// + this.to.getClass().getName());
|
||||
// }
|
||||
}
|
||||
return new GenericFieldInfo(this.target, this.propertyName, this.from,
|
||||
this.to, this.interpolator, this.setter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
abstract class AbstractFieldInfo<T> {
|
||||
protected Object object;
|
||||
|
||||
protected String fieldName;
|
||||
|
||||
protected PropertyGetter getter;
|
||||
protected PropertySetter setter;
|
||||
|
||||
protected T from;
|
||||
|
||||
protected T to;
|
||||
|
||||
AbstractFieldInfo(Object obj, String fieldName,
|
||||
PropertyGetter<T> pGetter, PropertySetter<T> pSetter) {
|
||||
this.object = obj;
|
||||
this.fieldName = fieldName;
|
||||
|
||||
this.getter = pGetter;
|
||||
this.setter = pSetter;
|
||||
}
|
||||
|
||||
void setValues(T from, T to) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
abstract void onStart();
|
||||
|
||||
abstract void updateFieldValue(float timelinePosition);
|
||||
}
|
||||
|
||||
private static <T> PropertyGetter<T> getPropertyGetter(Object obj,
|
||||
String fieldName, PropertyGetter<T> pGetter) {
|
||||
if (pGetter != null) {
|
||||
return pGetter;
|
||||
}
|
||||
return new DefaultPropertyGetter(obj, fieldName);
|
||||
}
|
||||
|
||||
private static <T> PropertySetter<T> getPropertySetter(Object obj,
|
||||
String fieldName, PropertySetter<T> pSetter) {
|
||||
if (pSetter != null) {
|
||||
return pSetter;
|
||||
}
|
||||
return new DefaultPropertySetter(obj, fieldName);
|
||||
}
|
||||
|
||||
private class GenericFieldInfoTo extends AbstractFieldInfo<Object> {
|
||||
private PropertyInterpolator propertyInterpolator;
|
||||
|
||||
private Object to;
|
||||
|
||||
GenericFieldInfoTo(Object obj, String fieldName, Object to,
|
||||
PropertyInterpolator propertyInterpolator,
|
||||
PropertyGetter propertyGetter, PropertySetter propertySetter) {
|
||||
super(obj, fieldName, getPropertyGetter(obj, fieldName,
|
||||
propertyGetter), getPropertySetter(obj, fieldName,
|
||||
propertySetter));
|
||||
this.propertyInterpolator = propertyInterpolator;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
@Override
|
||||
void onStart() {
|
||||
this.from = getter.get(object, fieldName);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateFieldValue(float timelinePosition) {
|
||||
try {
|
||||
Object value = this.propertyInterpolator.interpolate(from, to,
|
||||
timelinePosition);
|
||||
this.setter.set(this.object, this.fieldName, value);
|
||||
} catch (Throwable exc) {
|
||||
System.err.println("Exception occurred in updating field '"
|
||||
+ this.fieldName + "' of object "
|
||||
+ this.object.getClass().getCanonicalName()
|
||||
+ " at timeline position " + timelinePosition);
|
||||
exc.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GenericFieldInfo extends AbstractFieldInfo<Object> {
|
||||
private PropertyInterpolator propertyInterpolator;
|
||||
|
||||
GenericFieldInfo(Object obj, String fieldName, Object from, Object to,
|
||||
PropertyInterpolator propertyInterpolator,
|
||||
PropertySetter propertySetter) {
|
||||
super(obj, fieldName, null, getPropertySetter(obj, fieldName,
|
||||
propertySetter));
|
||||
this.propertyInterpolator = propertyInterpolator;
|
||||
this.setValues(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
void onStart() {
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateFieldValue(float timelinePosition) {
|
||||
try {
|
||||
Object value = this.propertyInterpolator.interpolate(from, to,
|
||||
timelinePosition);
|
||||
this.setter.set(this.object, this.fieldName, value);
|
||||
} catch (Throwable exc) {
|
||||
System.err.println("Exception occurred in updating field '"
|
||||
+ this.fieldName + "' of object "
|
||||
+ this.object.getClass().getCanonicalName()
|
||||
+ " at timeline position " + timelinePosition);
|
||||
exc.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class KeyFramesFieldInfo extends AbstractFieldInfo<Object> {
|
||||
KeyFrames keyFrames;
|
||||
|
||||
KeyFramesFieldInfo(Object obj, String fieldName, KeyFrames keyFrames,
|
||||
PropertySetter propertySetter) {
|
||||
super(obj, fieldName, null, getPropertySetter(obj, fieldName,
|
||||
propertySetter));
|
||||
this.keyFrames = keyFrames;
|
||||
}
|
||||
|
||||
@Override
|
||||
void onStart() {
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateFieldValue(float timelinePosition) {
|
||||
if (this.setter != null) {
|
||||
try {
|
||||
Object value = this.keyFrames.getValue(timelinePosition);
|
||||
this.setter.set(this.object, this.fieldName, value);
|
||||
} catch (Throwable exc) {
|
||||
exc.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Method getSetter(Object object, String propertyName) {
|
||||
String setterMethodName = "set"
|
||||
+ Character.toUpperCase(propertyName.charAt(0))
|
||||
+ propertyName.substring(1);
|
||||
Class oClazz = object.getClass();
|
||||
while (oClazz != null) {
|
||||
for (Method m : oClazz.getMethods()) {
|
||||
if (setterMethodName.equals(m.getName())
|
||||
&& (m.getParameterTypes().length == 1)
|
||||
&& (m.getReturnType() == Void.TYPE)
|
||||
&& (!Modifier.isStatic(m.getModifiers()))) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
oClazz = oClazz.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Method getGetter(Object object, String propertyName) {
|
||||
String getterMethodName = "get"
|
||||
+ Character.toUpperCase(propertyName.charAt(0))
|
||||
+ propertyName.substring(1);
|
||||
Class oClazz = object.getClass();
|
||||
while (oClazz != null) {
|
||||
for (Method m : oClazz.getMethods()) {
|
||||
if (getterMethodName.equals(m.getName())
|
||||
&& (m.getParameterTypes().length == 0)
|
||||
&& (!Modifier.isStatic(m.getModifiers()))) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
oClazz = oClazz.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import org.pushingpixels.trident.TimelineScenario.TimelineScenarioActor;
|
||||
|
||||
public abstract class TimelineRunnable implements Runnable,
|
||||
TimelineScenarioActor {
|
||||
private static ExecutorService service = new ThreadPoolExecutor(0,
|
||||
Integer.MAX_VALUE, 10L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>());
|
||||
|
||||
private Future<?> future;
|
||||
|
||||
@Override
|
||||
public void play() {
|
||||
this.future = service.submit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
if (this.future == null)
|
||||
return false;
|
||||
return this.future.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsReplay() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetDoneFlag() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.pushingpixels.trident.callback.TimelineScenarioCallback;
|
||||
|
||||
public class TimelineScenario {
|
||||
private Set<TimelineScenarioActor> waitingActors;
|
||||
|
||||
private Set<TimelineScenarioActor> runningActors;
|
||||
|
||||
private Set<TimelineScenarioActor> doneActors;
|
||||
|
||||
private Map<TimelineScenarioActor, Set<TimelineScenarioActor>> dependencies;
|
||||
|
||||
Chain callback;
|
||||
|
||||
TimelineScenarioState state;
|
||||
|
||||
TimelineScenarioState statePriorToSuspension;
|
||||
|
||||
boolean isLooping;
|
||||
|
||||
public enum TimelineScenarioState {
|
||||
DONE, PLAYING, IDLE, SUSPENDED
|
||||
}
|
||||
|
||||
class Chain implements TimelineScenarioCallback {
|
||||
private List<TimelineScenarioCallback> callbacks;
|
||||
|
||||
public Chain(TimelineScenarioCallback... callbacks) {
|
||||
this.callbacks = new ArrayList<TimelineScenarioCallback>();
|
||||
for (TimelineScenarioCallback callback : callbacks)
|
||||
this.callbacks.add(callback);
|
||||
}
|
||||
|
||||
public void addCallback(TimelineScenarioCallback callback) {
|
||||
this.callbacks.add(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineScenarioDone() {
|
||||
for (TimelineScenarioCallback callback : this.callbacks)
|
||||
callback.onTimelineScenarioDone();
|
||||
}
|
||||
}
|
||||
|
||||
public static interface TimelineScenarioActor {
|
||||
public boolean isDone();
|
||||
|
||||
public boolean supportsReplay();
|
||||
|
||||
public void resetDoneFlag();
|
||||
|
||||
public void play();
|
||||
}
|
||||
|
||||
public TimelineScenario() {
|
||||
this.waitingActors = new HashSet<TimelineScenarioActor>();
|
||||
this.runningActors = new HashSet<TimelineScenarioActor>();
|
||||
this.doneActors = new HashSet<TimelineScenarioActor>();
|
||||
|
||||
this.dependencies = new HashMap<TimelineScenarioActor, Set<TimelineScenarioActor>>();
|
||||
this.callback = new Chain();
|
||||
this.state = TimelineScenarioState.IDLE;
|
||||
}
|
||||
|
||||
public void addScenarioActor(TimelineScenarioActor actor) {
|
||||
if (actor.isDone()) {
|
||||
throw new IllegalArgumentException("Already finished");
|
||||
}
|
||||
this.waitingActors.add(actor);
|
||||
}
|
||||
|
||||
public void addCallback(TimelineScenarioCallback callback) {
|
||||
if (this.doneActors.size() > 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot change state of non-idle timeline scenario");
|
||||
}
|
||||
this.callback.addCallback(callback);
|
||||
}
|
||||
|
||||
private void checkDependencyParam(TimelineScenarioActor actor) {
|
||||
if (!waitingActors.contains(actor)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Must be first added with addScenarioActor() API");
|
||||
}
|
||||
}
|
||||
|
||||
public void addDependency(TimelineScenarioActor actor,
|
||||
TimelineScenarioActor... waitFor) {
|
||||
// check params
|
||||
this.checkDependencyParam(actor);
|
||||
for (TimelineScenarioActor wait : waitFor) {
|
||||
this.checkDependencyParam(wait);
|
||||
}
|
||||
|
||||
if (!this.dependencies.containsKey(actor))
|
||||
this.dependencies.put(actor, new HashSet<TimelineScenarioActor>());
|
||||
this.dependencies.get(actor).addAll(Arrays.asList(waitFor));
|
||||
}
|
||||
|
||||
private void checkDoneActors() {
|
||||
synchronized (TimelineEngine.LOCK) {
|
||||
for (Iterator<TimelineScenarioActor> itRunning = this.runningActors
|
||||
.iterator(); itRunning.hasNext();) {
|
||||
TimelineScenarioActor stillRunning = itRunning.next();
|
||||
if (stillRunning.isDone()) {
|
||||
itRunning.remove();
|
||||
this.doneActors.add(stillRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<TimelineScenarioActor> getReadyActors() {
|
||||
synchronized (TimelineEngine.LOCK) {
|
||||
if (this.state == TimelineScenarioState.SUSPENDED)
|
||||
return new HashSet<TimelineScenarioActor>();
|
||||
|
||||
this.checkDoneActors();
|
||||
|
||||
Set<TimelineScenarioActor> result = new HashSet<TimelineScenarioActor>();
|
||||
for (Iterator<TimelineScenarioActor> itWaiting = this.waitingActors
|
||||
.iterator(); itWaiting.hasNext();) {
|
||||
TimelineScenarioActor waitingActor = itWaiting.next();
|
||||
boolean canRun = true;
|
||||
Set<TimelineScenarioActor> toWaitFor = this.dependencies
|
||||
.get(waitingActor);
|
||||
if (toWaitFor != null) {
|
||||
for (TimelineScenarioActor actorToWaitFor : toWaitFor) {
|
||||
if (!doneActors.contains(actorToWaitFor)) {
|
||||
canRun = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canRun) {
|
||||
runningActors.add(waitingActor);
|
||||
itWaiting.remove();
|
||||
result.add(waitingActor);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.waitingActors.isEmpty() && this.runningActors.isEmpty()) {
|
||||
if (!this.isLooping) {
|
||||
this.state = TimelineScenarioState.DONE;
|
||||
} else {
|
||||
for (TimelineScenarioActor done : this.doneActors)
|
||||
done.resetDoneFlag();
|
||||
this.waitingActors.addAll(this.doneActors);
|
||||
this.doneActors.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
synchronized (TimelineEngine.LOCK) {
|
||||
TimelineScenarioState oldState = this.state;
|
||||
if (oldState != TimelineScenarioState.PLAYING)
|
||||
return;
|
||||
this.state = TimelineScenarioState.DONE;
|
||||
|
||||
for (TimelineScenarioActor waiting : this.waitingActors) {
|
||||
if (waiting instanceof Timeline) {
|
||||
((Timeline) waiting).cancel();
|
||||
}
|
||||
}
|
||||
for (TimelineScenarioActor running : this.runningActors) {
|
||||
if (running instanceof Timeline) {
|
||||
((Timeline) running).cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void suspend() {
|
||||
synchronized (TimelineEngine.LOCK) {
|
||||
TimelineScenarioState oldState = this.state;
|
||||
if (oldState != TimelineScenarioState.PLAYING)
|
||||
return;
|
||||
this.statePriorToSuspension = oldState;
|
||||
this.state = TimelineScenarioState.SUSPENDED;
|
||||
|
||||
for (TimelineScenarioActor running : this.runningActors) {
|
||||
if (running instanceof Timeline) {
|
||||
((Timeline) running).suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
synchronized (TimelineEngine.LOCK) {
|
||||
TimelineScenarioState oldState = this.state;
|
||||
if (oldState != TimelineScenarioState.SUSPENDED)
|
||||
return;
|
||||
this.state = this.statePriorToSuspension;
|
||||
|
||||
for (TimelineScenarioActor running : this.runningActors) {
|
||||
if (running instanceof Timeline) {
|
||||
((Timeline) running).resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void play() {
|
||||
this.isLooping = false;
|
||||
this.state = TimelineScenarioState.PLAYING;
|
||||
|
||||
TimelineEngine.getInstance().runTimelineScenario(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TimelineEngine.getInstance()
|
||||
.playScenario(TimelineScenario.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void playLoop() {
|
||||
for (TimelineScenarioActor actor : this.waitingActors) {
|
||||
if (!actor.supportsReplay())
|
||||
throw new UnsupportedOperationException(
|
||||
"Can't loop scenario with actor(s) that don't support replay");
|
||||
}
|
||||
this.isLooping = true;
|
||||
this.state = TimelineScenarioState.PLAYING;
|
||||
TimelineEngine.getInstance().runTimelineScenario(this, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TimelineEngine.getInstance()
|
||||
.playScenario(TimelineScenario.this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class Parallel extends TimelineScenario {
|
||||
@Override
|
||||
public void addDependency(TimelineScenarioActor actor,
|
||||
TimelineScenarioActor... waitFor) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Explicit dependencies not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sequence extends TimelineScenario {
|
||||
private TimelineScenarioActor lastActor;
|
||||
|
||||
@Override
|
||||
public void addDependency(TimelineScenarioActor actor,
|
||||
TimelineScenarioActor... waitFor) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Explicit dependencies not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScenarioActor(TimelineScenarioActor actor) {
|
||||
super.addScenarioActor(actor);
|
||||
if (this.lastActor != null) {
|
||||
super.addDependency(actor, this.lastActor);
|
||||
}
|
||||
this.lastActor = actor;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RendezvousSequence extends TimelineScenario {
|
||||
private Set<TimelineScenarioActor> addedSinceLastRendezvous;
|
||||
|
||||
private Set<TimelineScenarioActor> addedPriorToLastRendezvous;
|
||||
|
||||
public RendezvousSequence() {
|
||||
this.addedSinceLastRendezvous = new HashSet<TimelineScenarioActor>();
|
||||
this.addedPriorToLastRendezvous = new HashSet<TimelineScenarioActor>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDependency(TimelineScenarioActor actor,
|
||||
TimelineScenarioActor... waitFor) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Explicit dependencies not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScenarioActor(TimelineScenarioActor actor) {
|
||||
super.addScenarioActor(actor);
|
||||
this.addedSinceLastRendezvous.add(actor);
|
||||
}
|
||||
|
||||
public void rendezvous() {
|
||||
// make all actors added since last rendezvous to wait for
|
||||
// all actors added prior to last rendezvous
|
||||
if (this.addedPriorToLastRendezvous.size() > 0) {
|
||||
for (TimelineScenarioActor sinceLast : this.addedSinceLastRendezvous) {
|
||||
for (TimelineScenarioActor beforeLast : this.addedPriorToLastRendezvous) {
|
||||
super.addDependency(sinceLast, beforeLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.addedPriorToLastRendezvous.clear();
|
||||
this.addedPriorToLastRendezvous
|
||||
.addAll(this.addedSinceLastRendezvous);
|
||||
this.addedSinceLastRendezvous.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void play() {
|
||||
// add last implicit rendezvous
|
||||
this.rendezvous();
|
||||
super.play();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playLoop() {
|
||||
// add last implicit rendezvous
|
||||
this.rendezvous();
|
||||
super.playLoop();
|
||||
}
|
||||
}
|
||||
|
||||
public final TimelineScenarioState getState() {
|
||||
return this.state;
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
import org.pushingpixels.trident.TimelineEngine.TridentAnimationThread;
|
||||
import org.pushingpixels.trident.interpolator.PropertyInterpolator;
|
||||
import org.pushingpixels.trident.interpolator.PropertyInterpolatorSource;
|
||||
|
||||
@SuppressWarnings({"rawtypes","unchecked"})
|
||||
public class TridentConfig {
|
||||
private static TridentConfig config;
|
||||
|
||||
private Set<UIToolkitHandler> uiToolkitHandlers;
|
||||
|
||||
private Set<PropertyInterpolator> propertyInterpolators;
|
||||
|
||||
private TridentConfig.PulseSource pulseSource;
|
||||
|
||||
public interface PulseSource {
|
||||
public void waitUntilNextPulse();
|
||||
}
|
||||
|
||||
public static class FixedRatePulseSource implements
|
||||
TridentConfig.PulseSource {
|
||||
private int msDelay;
|
||||
|
||||
public FixedRatePulseSource(int msDelay) {
|
||||
this.msDelay = msDelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitUntilNextPulse() {
|
||||
try {
|
||||
Thread.sleep(this.msDelay);
|
||||
} catch (InterruptedException ie) {
|
||||
ie.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultPulseSource extends FixedRatePulseSource {
|
||||
DefaultPulseSource() {
|
||||
super(40);
|
||||
}
|
||||
}
|
||||
|
||||
private TridentConfig() {
|
||||
this.pulseSource = new DefaultPulseSource();
|
||||
|
||||
this.uiToolkitHandlers = new HashSet<UIToolkitHandler>();
|
||||
this.propertyInterpolators = new HashSet<PropertyInterpolator>();
|
||||
ClassLoader classLoader = Thread.currentThread()
|
||||
.getContextClassLoader();
|
||||
try {
|
||||
Enumeration urls = classLoader
|
||||
.getResources("META-INF/trident-plugin.properties");
|
||||
while (urls.hasMoreElements()) {
|
||||
URL pluginUrl = (URL) urls.nextElement();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(pluginUrl
|
||||
.openStream()));
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
String[] parts = line.split("=");
|
||||
if (parts.length != 2)
|
||||
continue;
|
||||
String key = parts[0];
|
||||
String value = parts[1];
|
||||
if ("UIToolkitHandler".compareTo(key) == 0) {
|
||||
try {
|
||||
Class pluginClass = classLoader
|
||||
.loadClass(value);
|
||||
if (pluginClass == null)
|
||||
continue;
|
||||
if (UIToolkitHandler.class
|
||||
.isAssignableFrom(pluginClass)) {
|
||||
UIToolkitHandler uiToolkitHandler = (UIToolkitHandler) pluginClass
|
||||
.newInstance();
|
||||
uiToolkitHandler.isHandlerFor(new Object());
|
||||
this.uiToolkitHandlers
|
||||
.add(uiToolkitHandler);
|
||||
}
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
// trying to initialize a plugin with a missing
|
||||
// class
|
||||
}
|
||||
}
|
||||
if ("PropertyInterpolatorSource".compareTo(key) == 0) {
|
||||
try {
|
||||
Class piSourceClass = classLoader
|
||||
.loadClass(value);
|
||||
if (piSourceClass == null)
|
||||
continue;
|
||||
if (PropertyInterpolatorSource.class
|
||||
.isAssignableFrom(piSourceClass)) {
|
||||
PropertyInterpolatorSource piSource = (PropertyInterpolatorSource) piSourceClass
|
||||
.newInstance();
|
||||
Set<PropertyInterpolator> interpolators = piSource
|
||||
.getPropertyInterpolators();
|
||||
for (PropertyInterpolator pi : interpolators) {
|
||||
try {
|
||||
Class basePropertyClass = pi
|
||||
.getBasePropertyClass();
|
||||
// is in classpath?
|
||||
basePropertyClass.getClass();
|
||||
this.propertyInterpolators.add(pi);
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
// trying to initialize a plugin
|
||||
// with a missing
|
||||
// class - just skip
|
||||
}
|
||||
|
||||
}
|
||||
// this.propertyInterpolators.addAll(piSource
|
||||
// .getPropertyInterpolators());
|
||||
}
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
// trying to initialize a plugin with a missing
|
||||
// class
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception exc) {
|
||||
exc.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized TridentConfig getInstance() {
|
||||
if (config == null)
|
||||
config = new TridentConfig();
|
||||
return config;
|
||||
}
|
||||
|
||||
public synchronized Collection<UIToolkitHandler> getUIToolkitHandlers() {
|
||||
return Collections.unmodifiableSet(this.uiToolkitHandlers);
|
||||
}
|
||||
|
||||
public synchronized Collection<PropertyInterpolator> getPropertyInterpolators() {
|
||||
return Collections.unmodifiableSet(this.propertyInterpolators);
|
||||
}
|
||||
|
||||
public synchronized PropertyInterpolator getPropertyInterpolator(
|
||||
Object... values) {
|
||||
for (PropertyInterpolator interpolator : this.propertyInterpolators) {
|
||||
try {
|
||||
Class basePropertyClass = interpolator.getBasePropertyClass();
|
||||
boolean hasMatch = true;
|
||||
for (Object value : values) {
|
||||
if (!basePropertyClass.isAssignableFrom(value.getClass())) {
|
||||
hasMatch = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (hasMatch)
|
||||
return interpolator;
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized void addPropertyInterpolator(
|
||||
PropertyInterpolator pInterpolator) {
|
||||
this.propertyInterpolators.add(pInterpolator);
|
||||
}
|
||||
|
||||
public synchronized void addPropertyInterpolatorSource(
|
||||
PropertyInterpolatorSource pInterpolatorSource) {
|
||||
this.propertyInterpolators.addAll(pInterpolatorSource
|
||||
.getPropertyInterpolators());
|
||||
}
|
||||
|
||||
public synchronized void removePropertyInterpolator(
|
||||
PropertyInterpolator pInterpolator) {
|
||||
this.propertyInterpolators.remove(pInterpolator);
|
||||
}
|
||||
|
||||
public synchronized void addUIToolkitHandler(
|
||||
UIToolkitHandler uiToolkitHandler) {
|
||||
this.uiToolkitHandlers.add(uiToolkitHandler);
|
||||
}
|
||||
|
||||
public synchronized void removeUIToolkitHandler(
|
||||
UIToolkitHandler uiToolkitHandler) {
|
||||
this.uiToolkitHandlers.remove(uiToolkitHandler);
|
||||
}
|
||||
|
||||
public synchronized void setPulseSource(PulseSource pulseSource) {
|
||||
TridentAnimationThread current = TimelineEngine.getInstance().animatorThread;
|
||||
if ((current != null) && current.isAlive())
|
||||
throw new IllegalStateException(
|
||||
"Cannot replace the pulse source thread once it's running");
|
||||
this.pulseSource = pulseSource;
|
||||
}
|
||||
|
||||
public synchronized TridentConfig.PulseSource getPulseSource() {
|
||||
return pulseSource;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident;
|
||||
|
||||
public interface UIToolkitHandler {
|
||||
public boolean isHandlerFor(Object mainTimelineObject);
|
||||
|
||||
public boolean isInReadyState(Object mainTimelineObject);
|
||||
|
||||
public void runOnUIThread(Object mainTimelineObject, Runnable runnable);
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.callback;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to mark code that should run on UI thread.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RunOnUIThread {
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.callback;
|
||||
|
||||
import org.pushingpixels.trident.Timeline.TimelineState;
|
||||
|
||||
/**
|
||||
* Callback for the fade tracker. Is used when the application (some UI
|
||||
* delegate) wishes to execute some code on the fade.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
public interface TimelineCallback {
|
||||
/**
|
||||
* Indicates that the timeline state has changed.
|
||||
*
|
||||
* @param oldState
|
||||
* The old timeline state.
|
||||
* @param newState
|
||||
* The new timeline state.
|
||||
* @param durationFraction
|
||||
* The current timeline duration fraction.Is guaranteed to be in
|
||||
* 0.0-1.0 range. The rate of change of this value is linear, and
|
||||
* the value is proportional to
|
||||
* {@link Timeline#setDuration(long)}.
|
||||
* @param timelinePosition
|
||||
* The current timeline position. Is guaranteed to be in 0.0-1.0
|
||||
* range. The rate of change of this value is not necessarily
|
||||
* linear and is affected by the
|
||||
* {@link Timeline#setEase(org.pushingpixels.trident.ease.TimelineEase)}
|
||||
* .
|
||||
*/
|
||||
public void onTimelineStateChanged(TimelineState oldState,
|
||||
TimelineState newState, float durationFraction,
|
||||
float timelinePosition);
|
||||
|
||||
/**
|
||||
* Indicates that the timeline pulse has happened.
|
||||
*
|
||||
* @param durationFraction
|
||||
* The current timeline duration fraction.Is guaranteed to be in
|
||||
* 0.0-1.0 range. The rate of change of this value is linear, and
|
||||
* the value is proportional to
|
||||
* {@link Timeline#setDuration(long)}.
|
||||
* @param timelinePosition
|
||||
* The current timeline position. Is guaranteed to be in 0.0-1.0
|
||||
* range. The rate of change of this value is not necessarily
|
||||
* linear and is affected by the
|
||||
* {@link Timeline#setEase(org.pushingpixels.trident.ease.TimelineEase)}
|
||||
* .
|
||||
*/
|
||||
public void onTimelinePulse(float durationFraction, float timelinePosition);
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.callback;
|
||||
|
||||
import org.pushingpixels.trident.Timeline.TimelineState;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link TimelineCallback} that does nothing.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
public class TimelineCallbackAdapter implements TimelineCallback {
|
||||
@Override
|
||||
public void onTimelineStateChanged(TimelineState oldState,
|
||||
TimelineState newState, float durationFraction,
|
||||
float timelinePosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelinePulse(float durationFraction, float timelinePosition) {
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.callback;
|
||||
|
||||
/**
|
||||
* Callback for tracking the {@link TimelineScenario}s.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
public interface TimelineScenarioCallback {
|
||||
/**
|
||||
* Indicates that the all timelines and swing workers in the timeline
|
||||
* scenario have finished.
|
||||
*/
|
||||
public void onTimelineScenarioDone();
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.callback;
|
||||
|
||||
/**
|
||||
* Empty implementation of {@link TimelineCallback} that does nothing but is
|
||||
* marked to run on the EDT.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
@RunOnUIThread
|
||||
public class UIThreadTimelineCallbackAdapter extends TimelineCallbackAdapter {
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.ease;
|
||||
|
||||
public class Linear implements TimelineEase {
|
||||
@Override
|
||||
public float map(float durationFraction) {
|
||||
return durationFraction;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.ease;
|
||||
|
||||
public class Sine implements TimelineEase {
|
||||
@Override
|
||||
public float map(float durationFraction) {
|
||||
return (float) Math.sin(durationFraction * Math.PI / 2.0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.ease;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Spline easer. Is based on the code from <a
|
||||
* href="https://timingframework.dev.java.net">TimingFramework</a> by Chet Haase
|
||||
* and Romain Guy.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
@SuppressWarnings({"unchecked","rawtypes","unused"})
|
||||
public class Spline implements TimelineEase {
|
||||
// private float easeAmount;
|
||||
|
||||
public Spline(float easeAmount) {
|
||||
this(easeAmount, 0, 1 - easeAmount, 1);
|
||||
// this.easeAmount = easeAmount;
|
||||
}
|
||||
|
||||
private static class FloatPoint {
|
||||
public float x;
|
||||
public float y;
|
||||
|
||||
public FloatPoint(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: (x0,y0) and (x1,y1) are implicitly (0, 0) and (1,1) respectively
|
||||
private float x1, y1, x2, y2;
|
||||
private ArrayList lengths = new ArrayList();
|
||||
|
||||
/**
|
||||
* Creates a new instance of SplineInterpolator with the control points
|
||||
* defined by (x1, y1) and (x2, y2). The anchor points are implicitly
|
||||
* defined as (0, 0) and (1, 1).
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* This exception is thrown when values beyond the allowed [0,1]
|
||||
* range are passed in
|
||||
*/
|
||||
|
||||
public Spline(float x1, float y1, float x2, float y2) {
|
||||
if (x1 < 0 || x1 > 1.0f || y1 < 0 || y1 > 1.0f || x2 < 0 || x2 > 1.0f
|
||||
|| y2 < 0 || y2 > 1.0f) {
|
||||
throw new IllegalArgumentException("Control points must be in "
|
||||
+ "the range [0, 1]:");
|
||||
}
|
||||
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
|
||||
// Now contruct the array of all lengths to t in [0, 1.0]
|
||||
float prevX = 0.0f;
|
||||
float prevY = 0.0f;
|
||||
float prevLength = 0.0f; // cumulative length
|
||||
for (float t = 0.01f; t <= 1.0f; t += .01f) {
|
||||
FloatPoint xy = getXY(t);
|
||||
float length = prevLength
|
||||
+ (float) Math.sqrt((xy.x - prevX) * (xy.x - prevX)
|
||||
+ (xy.y - prevY) * (xy.y - prevY));
|
||||
LengthItem lengthItem = new LengthItem(length, t);
|
||||
lengths.add(lengthItem);
|
||||
prevLength = length;
|
||||
prevX = xy.x;
|
||||
prevY = xy.y;
|
||||
}
|
||||
// Now calculate the fractions so that we can access the lengths
|
||||
// array with values in [0,1]. prevLength now holds the total
|
||||
// length of the spline.
|
||||
for (int i = 0; i < lengths.size(); ++i) {
|
||||
LengthItem lengthItem = (LengthItem) lengths.get(i);
|
||||
lengthItem.setFraction(prevLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the XY point for a given t value.
|
||||
*
|
||||
* The general spline equation is: x = b0*x0 + b1*x1 + b2*x2 + b3*x3 y =
|
||||
* b0*y0 + b1*y1 + b2*y2 + b3*y3 where: b0 = (1-t)^3 b1 = 3 * t * (1-t)^2 b2
|
||||
* = 3 * t^2 * (1-t) b3 = t^3 We know that (x0,y0) == (0,0) and (x1,y1) ==
|
||||
* (1,1) for our splines, so this simplifies to: x = b1*x1 + b2*x2 + b3 y =
|
||||
* b1*x1 + b2*x2 + b3
|
||||
*
|
||||
* @param t
|
||||
* parametric value for spline calculation
|
||||
*/
|
||||
private FloatPoint getXY(float t) {
|
||||
FloatPoint xy;
|
||||
float invT = (1 - t);
|
||||
float b1 = 3 * t * (invT * invT);
|
||||
float b2 = 3 * (t * t) * invT;
|
||||
float b3 = t * t * t;
|
||||
xy = new FloatPoint((b1 * x1) + (b2 * x2) + b3, (b1 * y1)
|
||||
+ (b2 * y2) + b3);
|
||||
return xy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function: When we are evaluating the spline, we only care about
|
||||
* the Y values. See {@link getXY getXY} for the details.
|
||||
*/
|
||||
private float getY(float t) {
|
||||
FloatPoint xy;
|
||||
float invT = (1 - t);
|
||||
float b1 = 3 * t * (invT * invT);
|
||||
float b2 = 3 * (t * t) * invT;
|
||||
float b3 = t * t * t;
|
||||
return (b1 * y1) + (b2 * y2) + b3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a fraction of time along the spline (which we can interpret as the
|
||||
* length along a spline), return the interpolated value of the spline. We
|
||||
* first calculate the t value for the length (by doing a lookup in our
|
||||
* array of previousloy calculated values and then linearly interpolating
|
||||
* between the nearest values) and then calculate the Y value for this t.
|
||||
*
|
||||
* @param lengthFraction
|
||||
* Fraction of time in a given time interval.
|
||||
* @return interpolated fraction between 0 and 1
|
||||
*/
|
||||
public float map(float lengthFraction) {
|
||||
// REMIND: speed this up with binary search
|
||||
float interpolatedT = 1.0f;
|
||||
float prevT = 0.0f;
|
||||
float prevLength = 0.0f;
|
||||
for (int i = 0; i < lengths.size(); ++i) {
|
||||
LengthItem lengthItem = (LengthItem) lengths.get(i);
|
||||
float fraction = lengthItem.getFraction();
|
||||
float t = lengthItem.getT();
|
||||
if (lengthFraction <= fraction) {
|
||||
// answer lies between last item and this one
|
||||
float proportion = (lengthFraction - prevLength)
|
||||
/ (fraction - prevLength);
|
||||
interpolatedT = prevT + proportion * (t - prevT);
|
||||
return getY(interpolatedT);
|
||||
}
|
||||
prevLength = fraction;
|
||||
prevT = t;
|
||||
}
|
||||
return getY(interpolatedT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Struct used to store information about length values. Specifically, each item
|
||||
* stores the "length" (which can be thought of as the time elapsed along the
|
||||
* spline path), the "t" value at this length (used to calculate the (x,y) point
|
||||
* along the spline), and the "fraction" which is equal to the length divided by
|
||||
* the total absolute length of the spline. After we calculate all LengthItems
|
||||
* for a give spline, we have a list of entries which can return the t values
|
||||
* for fractional lengths from 0 to 1.
|
||||
*/
|
||||
class LengthItem {
|
||||
float length;
|
||||
float t;
|
||||
float fraction;
|
||||
|
||||
LengthItem(float length, float t, float fraction) {
|
||||
this.length = length;
|
||||
this.t = t;
|
||||
this.fraction = fraction;
|
||||
}
|
||||
|
||||
LengthItem(float length, float t) {
|
||||
this.length = length;
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
public float getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public float getT() {
|
||||
return t;
|
||||
}
|
||||
|
||||
public float getFraction() {
|
||||
return fraction;
|
||||
}
|
||||
|
||||
void setFraction(float totalLength) {
|
||||
fraction = length / totalLength;
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.ease;
|
||||
|
||||
public interface TimelineEase {
|
||||
public float map(float durationFraction);
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.interpolator;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class CorePropertyInterpolators implements PropertyInterpolatorSource {
|
||||
private Set<PropertyInterpolator> interpolators;
|
||||
|
||||
public CorePropertyInterpolators() {
|
||||
this.interpolators = new HashSet<PropertyInterpolator>();
|
||||
this.interpolators.add(new IntegerPropertyInterpolator());
|
||||
this.interpolators.add(new FloatPropertyInterpolator());
|
||||
this.interpolators.add(new DoublePropertyInterpolator());
|
||||
this.interpolators.add(new LongPropertyInterpolator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PropertyInterpolator> getPropertyInterpolators() {
|
||||
return Collections.unmodifiableSet(this.interpolators);
|
||||
}
|
||||
|
||||
private static class FloatPropertyInterpolator implements
|
||||
PropertyInterpolator<Float> {
|
||||
@Override
|
||||
public Class getBasePropertyClass() {
|
||||
return Float.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float interpolate(Float from, Float to, float timelinePosition) {
|
||||
return from + (to - from) * timelinePosition;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DoublePropertyInterpolator implements
|
||||
PropertyInterpolator<Double> {
|
||||
@Override
|
||||
public Class getBasePropertyClass() {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double interpolate(Double from, Double to, float timelinePosition) {
|
||||
return from + (to - from) * timelinePosition;
|
||||
}
|
||||
}
|
||||
|
||||
private static class IntegerPropertyInterpolator implements
|
||||
PropertyInterpolator<Integer> {
|
||||
@Override
|
||||
public Class getBasePropertyClass() {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer interpolate(Integer from, Integer to,
|
||||
float timelinePosition) {
|
||||
return (int) (from + (to - from) * timelinePosition);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LongPropertyInterpolator implements
|
||||
PropertyInterpolator<Long> {
|
||||
@Override
|
||||
public Class getBasePropertyClass() {
|
||||
return Long.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long interpolate(Long from, Long to, float timelinePosition) {
|
||||
return (long) (from + (to - from) * timelinePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* Copyright (c) 2006, Sun Microsystems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of the TimingFramework project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.pushingpixels.trident.interpolator;
|
||||
|
||||
import org.pushingpixels.trident.ease.TimelineEase;
|
||||
|
||||
/**
|
||||
*
|
||||
* KeyFrames holds information about the times at which values are sampled
|
||||
* (KeyTimes) and the values at those times (KeyValues). It also holds
|
||||
* information about how to interpolate between these values for times that lie
|
||||
* between the sampling points.
|
||||
*
|
||||
* @author Chet
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class KeyFrames<T> {
|
||||
|
||||
private KeyValues<T> keyValues;
|
||||
private KeyTimes keyTimes;
|
||||
private KeyInterpolators interpolators;
|
||||
|
||||
/**
|
||||
* Simplest variation; determine keyTimes based on even division of 0-1
|
||||
* range based on number of keyValues. This constructor assumes LINEAR
|
||||
* interpolation.
|
||||
*
|
||||
* @param keyValues
|
||||
* values that will be assumed at each time in keyTimes
|
||||
*/
|
||||
public KeyFrames(KeyValues<T> keyValues) {
|
||||
init(keyValues, null, (TimelineEase) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This variant takes both keyValues (values at each point in time) and
|
||||
* keyTimes (times at which values are sampled).
|
||||
*
|
||||
* @param keyValues
|
||||
* values that the animation will assume at each of the
|
||||
* corresponding times in keyTimes
|
||||
* @param keyTimes
|
||||
* times at which the animation will assume the corresponding
|
||||
* values in keyValues
|
||||
* @throws IllegalArgumentException
|
||||
* keyTimes and keySizes must have the same number of elements
|
||||
* since these structures are meant to have corresponding
|
||||
* entries; an exception is thrown otherwise.
|
||||
*/
|
||||
public KeyFrames(KeyValues<T> keyValues, KeyTimes keyTimes) {
|
||||
init(keyValues, keyTimes, (TimelineEase) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full constructor: caller provides an instance of all key* structures
|
||||
* which will be used to calculate between all times in the keyTimes list. A
|
||||
* null interpolator parameter is equivalent to calling
|
||||
* {@link KeyFrames#KeyFrames(KeyValues, KeyTimes)}.
|
||||
*
|
||||
* @param keyValues
|
||||
* values that the animation will assume at each of the
|
||||
* corresponding times in keyTimes
|
||||
* @param keyTimes
|
||||
* times at which the animation will assume the corresponding
|
||||
* values in keyValues
|
||||
* @param interpolators
|
||||
* collection of Interpolators that control the calculation of
|
||||
* values in each of the intervals defined by keyFrames. If this
|
||||
* value is null, a {@link LinearInterpolator} will be used for
|
||||
* all intervals. If there is only one interpolator, that
|
||||
* interpolator will be used for all intervals. Otherwise, there
|
||||
* must be a number of interpolators equal to the number of
|
||||
* intervals (which is one less than the number of keyTimes).
|
||||
* @throws IllegalArgumentException
|
||||
* keyTimes and keyValues must have the same number of elements
|
||||
* since these structures are meant to have corresponding
|
||||
* entries; an exception is thrown otherwise.
|
||||
* @throws IllegalArgumentException
|
||||
* The number of interpolators must either be zero
|
||||
* (interpolators == null), one, or one less than the size of
|
||||
* keyTimes.
|
||||
*/
|
||||
public KeyFrames(KeyValues<T> keyValues, KeyTimes keyTimes,
|
||||
TimelineEase... interpolators) {
|
||||
init(keyValues, keyTimes, interpolators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility constructor that assumes even division of times according to size
|
||||
* of keyValues and interpolation according to interpolators parameter.
|
||||
*
|
||||
* @param keyValues
|
||||
* values that the animation will assume at each of the
|
||||
* corresponding times in keyTimes
|
||||
* @param interpolators
|
||||
* collection of Interpolators that control the calculation of
|
||||
* values in each of the intervals defined by keyFrames. If this
|
||||
* value is null, a {@link LinearInterpolator} will be used for
|
||||
* all intervals. If there is only one interpolator, that
|
||||
* interpolator will be used for all intervals. Otherwise, there
|
||||
* must be a number of interpolators equal to the number of
|
||||
* intervals (which is one less than the number of keyTimes).
|
||||
* @throws IllegalArgumentException
|
||||
* The number of interpolators must either be zero
|
||||
* (interpolators == null), one, or one less than the size of
|
||||
* keyTimes.
|
||||
*/
|
||||
public KeyFrames(KeyValues<T> keyValues, TimelineEase... interpolators) {
|
||||
init(keyValues, null, interpolators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function called by constructors to perform common initialization
|
||||
* chores
|
||||
*/
|
||||
private void init(KeyValues<T> keyValues, KeyTimes keyTimes,
|
||||
TimelineEase... interpolators) {
|
||||
int numFrames = keyValues.getSize();
|
||||
// If keyTimes null, create our own
|
||||
if (keyTimes == null) {
|
||||
float keyTimesArray[] = new float[numFrames];
|
||||
float timeVal = 0.0f;
|
||||
keyTimesArray[0] = timeVal;
|
||||
for (int i = 1; i < (numFrames - 1); ++i) {
|
||||
timeVal += (1.0f / (numFrames - 1));
|
||||
keyTimesArray[i] = timeVal;
|
||||
}
|
||||
keyTimesArray[numFrames - 1] = 1.0f;
|
||||
this.keyTimes = new KeyTimes(keyTimesArray);
|
||||
} else {
|
||||
this.keyTimes = keyTimes;
|
||||
}
|
||||
this.keyValues = keyValues;
|
||||
if (numFrames != this.keyTimes.getSize()) {
|
||||
throw new IllegalArgumentException("keyValues and keyTimes"
|
||||
+ " must be of equal size");
|
||||
}
|
||||
if (interpolators != null && (interpolators.length != (numFrames - 1))
|
||||
&& (interpolators.length != 1)) {
|
||||
throw new IllegalArgumentException(
|
||||
"interpolators must be "
|
||||
+ "either null (implying interpolation for all intervals), "
|
||||
+ "a single interpolator (which will be used for all "
|
||||
+ "intervals), or a number of interpolators equal to "
|
||||
+ "one less than the number of times.");
|
||||
}
|
||||
this.interpolators = new KeyInterpolators(numFrames - 1, interpolators);
|
||||
}
|
||||
|
||||
public Class getType() {
|
||||
return keyValues.getType();
|
||||
}
|
||||
|
||||
KeyValues getKeyValues() {
|
||||
return keyValues;
|
||||
}
|
||||
|
||||
KeyTimes getKeyTimes() {
|
||||
return keyTimes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns time interval that contains this time fraction
|
||||
*/
|
||||
public int getInterval(float fraction) {
|
||||
return keyTimes.getInterval(fraction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value for the given fraction elapsed of the animation cycle.
|
||||
* Given the fraction, this method will determine what interval the fraction
|
||||
* lies within, how much of that interval has elapsed, what the boundary
|
||||
* values are (from KeyValues), what the interpolated fraction is (from the
|
||||
* Interpolator for the interval), and what the final interpolated
|
||||
* intermediate value is (using the appropriate Evaluator). This method will
|
||||
* call into the Interpolator for the time interval to get the interpolated
|
||||
* method. To ensure that future operations succeed, the value received from
|
||||
* the interpolation will be clamped to the interval [0,1].
|
||||
*/
|
||||
public Object getValue(float fraction) {
|
||||
// First, figure out the real fraction to use, given the
|
||||
// interpolation type and keyTimes
|
||||
int interval = getInterval(fraction);
|
||||
float t0 = keyTimes.getTime(interval);
|
||||
float t1 = keyTimes.getTime(interval + 1);
|
||||
float t = (fraction - t0) / (t1 - t0);
|
||||
float interpolatedT = interpolators.interpolate(interval, t);
|
||||
// clamp to avoid problems with buggy Interpolators
|
||||
if (interpolatedT < 0f) {
|
||||
interpolatedT = 0f;
|
||||
} else if (interpolatedT > 1f) {
|
||||
interpolatedT = 1f;
|
||||
}
|
||||
return keyValues.getValue(interval, (interval + 1), interpolatedT);
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright (c) 2006, Sun Microsystems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of the TimingFramework project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.pushingpixels.trident.interpolator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.pushingpixels.trident.ease.Linear;
|
||||
import org.pushingpixels.trident.ease.TimelineEase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Chet
|
||||
*/
|
||||
class KeyInterpolators {
|
||||
|
||||
private ArrayList<TimelineEase> interpolators = new ArrayList<TimelineEase>();
|
||||
|
||||
/**
|
||||
* Creates a new instance of KeyInterpolators
|
||||
*/
|
||||
KeyInterpolators(int numIntervals, TimelineEase... interpolators) {
|
||||
if (interpolators == null || interpolators[0] == null) {
|
||||
for (int i = 0; i < numIntervals; ++i) {
|
||||
this.interpolators.add(new Linear());
|
||||
}
|
||||
} else if (interpolators.length < numIntervals) {
|
||||
for (int i = 0; i < numIntervals; ++i) {
|
||||
this.interpolators.add(interpolators[0]);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < numIntervals; ++i) {
|
||||
this.interpolators.add(interpolators[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float interpolate(int interval, float fraction) {
|
||||
return interpolators.get(interval).map(fraction);
|
||||
}
|
||||
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright (c) 2005-2006, Sun Microsystems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of the TimingFramework project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.pushingpixels.trident.interpolator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Stores a list of times from 0 to 1 (the elapsed fraction of an animation
|
||||
* cycle) that are used in calculating interpolated
|
||||
* values for PropertySetter given a matching set of KeyValues and
|
||||
* Interpolators for those time intervals. In the simplest case, a
|
||||
* KeyFrame will consist of just two times in KeyTimes: 0 and 1.
|
||||
*
|
||||
* @author Chet
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class KeyTimes {
|
||||
|
||||
private ArrayList<Float> times = new ArrayList<Float>();
|
||||
|
||||
/**
|
||||
* Creates a new instance of KeyTimes. Times should be in increasing
|
||||
* order and should all be in the range [0,1], with the first value
|
||||
* being zero and the last being 1
|
||||
* @throws IllegalArgumentException Time values must be ordered in
|
||||
* increasing value, the first value must be 0 and the last value
|
||||
* must be 1
|
||||
*/
|
||||
public KeyTimes(float... times) {
|
||||
if (times[0] != 0) {
|
||||
throw new IllegalArgumentException("First time value must" +
|
||||
" be zero");
|
||||
}
|
||||
if (times[times.length - 1] != 1.0f) {
|
||||
throw new IllegalArgumentException("Last time value must" +
|
||||
" be one");
|
||||
}
|
||||
float prevTime = 0;
|
||||
for (float time : times) {
|
||||
if (time < prevTime) {
|
||||
throw new IllegalArgumentException("Time values must be" +
|
||||
" in increasing order");
|
||||
}
|
||||
this.times.add(time);
|
||||
prevTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList getTimes() {
|
||||
return times;
|
||||
}
|
||||
|
||||
int getSize() {
|
||||
return times.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns time interval that contains this time fraction
|
||||
*/
|
||||
int getInterval(float fraction) {
|
||||
int prevIndex = 0;
|
||||
for (int i = 1; i < times.size(); ++i) {
|
||||
float time = times.get(i);
|
||||
if (time >= fraction) {
|
||||
// inclusive of start time at next interval. So fraction==1
|
||||
// will return the final interval (times.size() - 1)
|
||||
return prevIndex;
|
||||
}
|
||||
prevIndex = i;
|
||||
}
|
||||
return prevIndex;
|
||||
}
|
||||
|
||||
float getTime(int index) {
|
||||
return times.get(index);
|
||||
}
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* Copyright (c) 2006, Sun Microsystems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of the TimingFramework project nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.pushingpixels.trident.interpolator;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.pushingpixels.trident.TridentConfig;
|
||||
|
||||
/**
|
||||
* Stores a list of values that correspond to the times in a {@link KeyTimes}
|
||||
* object. These structures are then used to create a {@link KeyFrames} object,
|
||||
* which is then used to create a {@link PropertySetter} for the purposes of
|
||||
* modifying an object's property over time.
|
||||
* <p>
|
||||
* At each of the times in {@link KeyTimes}, the property will take on the
|
||||
* corresponding value in the KeyValues object. Between these times, the
|
||||
* property will take on a value based on the interpolation information stored
|
||||
* in the KeyFrames object and the {@link Evaluator} for the type of the values
|
||||
* in KeyValues.
|
||||
* <p>
|
||||
* This class has built-in support for various known types, as defined in
|
||||
* {@link Evaluator}.
|
||||
* <p>
|
||||
* For a simple example using KeyValues to create a KeyFrames and PropertySetter
|
||||
* object, see the class header comments in {@link PropertySetter}.
|
||||
*
|
||||
*
|
||||
* @author Chet
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes","unchecked"})
|
||||
public class KeyValues<T> {
|
||||
|
||||
private final List<T> values = new ArrayList<T>();
|
||||
private final PropertyInterpolator<T> evaluator;
|
||||
private final Class<?> type;
|
||||
private T startValue;
|
||||
|
||||
/**
|
||||
* Constructs a KeyValues object from one or more values. The internal
|
||||
* Evaluator is automatically determined by the type of the parameters.
|
||||
*
|
||||
* @param params
|
||||
* the values to interpolate between. If there is only one
|
||||
* parameter, this is assumed to be a "to" animation where the
|
||||
* first value is dynamically determined at runtime when the
|
||||
* animation is started.
|
||||
* @throws IllegalArgumentException
|
||||
* if an {@link Evaluator} cannot be found that can interpolate
|
||||
* between the value types supplied
|
||||
*/
|
||||
public static <T> KeyValues<T> create(T... params) {
|
||||
return new KeyValues(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a KeyValues object from a Evaluator and one or more values.
|
||||
*
|
||||
* @param params
|
||||
* the values to interpolate between. If there is only one
|
||||
* parameter, this is assumed to be a "to" animation where the
|
||||
* first value is dynamically determined at runtime when the
|
||||
* animation is started.
|
||||
* @throws IllegalArgumentException
|
||||
* if params does not have at least one value.
|
||||
*/
|
||||
public static <T> KeyValues<T> create(PropertyInterpolator evaluator,
|
||||
T... params) {
|
||||
return new KeyValues(evaluator, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor, called by factory method
|
||||
*/
|
||||
private KeyValues(T... params) {
|
||||
this(TridentConfig.getInstance().getPropertyInterpolator(params), params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor, called by factory method
|
||||
*/
|
||||
private KeyValues(PropertyInterpolator evaluator, T... params) {
|
||||
if (params == null) {
|
||||
throw new IllegalArgumentException("params array cannot be null");
|
||||
} else if (params.length == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"params array must have at least one element");
|
||||
}
|
||||
if (params.length == 1) {
|
||||
// this is a "to" animation; set first element to null
|
||||
values.add(null);
|
||||
}
|
||||
Collections.addAll(values, params);
|
||||
this.type = params.getClass().getComponentType();
|
||||
this.evaluator = evaluator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of values stored in this object.
|
||||
*
|
||||
* @return the number of values stored in this object
|
||||
*/
|
||||
int getSize() {
|
||||
return values.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data type of the values stored in this object.
|
||||
*
|
||||
* @return a Class value representing the type of values stored in this
|
||||
* object
|
||||
*/
|
||||
Class<?> getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called at start of animation; sets starting value in simple "to"
|
||||
* animations.
|
||||
*/
|
||||
void setStartValue(T startValue) {
|
||||
if (isToAnimation()) {
|
||||
this.startValue = startValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for determining whether this is a "to" animation (true if
|
||||
* the first value is null).
|
||||
*/
|
||||
boolean isToAnimation() {
|
||||
return (values.get(0) == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value calculated from the value at the lower index, the value at
|
||||
* the upper index, the fraction elapsed between these endpoints, and the
|
||||
* evaluator set up by this object at construction time.
|
||||
*/
|
||||
T getValue(int i0, int i1, float fraction) {
|
||||
T value;
|
||||
T lowerValue = values.get(i0);
|
||||
if (lowerValue == null) {
|
||||
// "to" animation
|
||||
lowerValue = startValue;
|
||||
}
|
||||
if (i0 == i1) {
|
||||
// trivial case
|
||||
value = lowerValue;
|
||||
} else {
|
||||
T v0 = lowerValue;
|
||||
T v1 = values.get(i1);
|
||||
value = evaluator.interpolate(v0, v1, fraction);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.interpolator;
|
||||
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
public interface PropertyInterpolator<T> {
|
||||
public Class getBasePropertyClass();
|
||||
|
||||
public T interpolate(T from, T to, float timelinePosition);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.interpolator;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public interface PropertyInterpolatorSource {
|
||||
public Set<PropertyInterpolator> getPropertyInterpolators();
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.swt;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.eclipse.swt.graphics.*;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.pushingpixels.trident.interpolator.PropertyInterpolator;
|
||||
import org.pushingpixels.trident.interpolator.PropertyInterpolatorSource;
|
||||
|
||||
/**
|
||||
* Built-in interpolators for SWT classes.
|
||||
*
|
||||
* @author Kirill Grouchnikov
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class SWTPropertyInterpolators implements PropertyInterpolatorSource {
|
||||
private Set<PropertyInterpolator> interpolators;
|
||||
|
||||
public SWTPropertyInterpolators() {
|
||||
this.interpolators = new HashSet<PropertyInterpolator>();
|
||||
this.interpolators.add(new ColorInterpolator());
|
||||
this.interpolators.add(new PointInterpolator());
|
||||
this.interpolators.add(new RectangleInterpolator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PropertyInterpolator> getPropertyInterpolators() {
|
||||
return Collections.unmodifiableSet(this.interpolators);
|
||||
}
|
||||
|
||||
static class ColorInterpolator implements PropertyInterpolator<Color> {
|
||||
@Override
|
||||
public Class getBasePropertyClass() {
|
||||
return Color.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color interpolate(Color from, Color to, float timelinePosition) {
|
||||
return getInterpolatedColor(from, to, 1.0f - timelinePosition);
|
||||
}
|
||||
|
||||
RGB getInterpolatedRGB(Color color1, Color color2, float color1Likeness) {
|
||||
if ((color1Likeness < 0.0) || (color1Likeness > 1.0))
|
||||
throw new IllegalArgumentException(
|
||||
"Color likeness should be in 0.0-1.0 range [is "
|
||||
+ color1Likeness + "]");
|
||||
int lr = color1.getRed();
|
||||
int lg = color1.getGreen();
|
||||
int lb = color1.getBlue();
|
||||
int dr = color2.getRed();
|
||||
int dg = color2.getGreen();
|
||||
int db = color2.getBlue();
|
||||
|
||||
// using some interpolation values (such as 0.29 from issue 401)
|
||||
// results in an incorrect final value without Math.round.
|
||||
int r = (lr == dr) ? lr : (int) Math.round(color1Likeness * lr
|
||||
+ (1.0 - color1Likeness) * dr);
|
||||
int g = (lg == dg) ? lg : (int) Math.round(color1Likeness * lg
|
||||
+ (1.0 - color1Likeness) * dg);
|
||||
int b = (lb == db) ? lb : (int) Math.round(color1Likeness * lb
|
||||
+ (1.0 - color1Likeness) * db);
|
||||
|
||||
return new RGB(r, g, b);
|
||||
}
|
||||
|
||||
Color getInterpolatedColor(Color color1, Color color2,
|
||||
float color1Likeness) {
|
||||
if (color1.equals(color2))
|
||||
return color1;
|
||||
if (color1Likeness == 1.0)
|
||||
return color1;
|
||||
if (color1Likeness == 0.0)
|
||||
return color2;
|
||||
return new Color(Display.getDefault(), getInterpolatedRGB(color1,
|
||||
color2, color1Likeness));
|
||||
}
|
||||
}
|
||||
|
||||
static class PointInterpolator implements PropertyInterpolator<Point> {
|
||||
public Point interpolate(Point from, Point to, float timelinePosition) {
|
||||
int x = from.x + (int) (timelinePosition * (to.x - from.x));
|
||||
int y = from.y + (int) (timelinePosition * (to.y - from.y));
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getBasePropertyClass() {
|
||||
return Point.class;
|
||||
}
|
||||
}
|
||||
|
||||
static class RectangleInterpolator implements
|
||||
PropertyInterpolator<Rectangle> {
|
||||
public Rectangle interpolate(Rectangle from, Rectangle to,
|
||||
float timelinePosition) {
|
||||
int x = from.x + (int) (timelinePosition * (to.x - from.x));
|
||||
int y = from.y + (int) (timelinePosition * (to.y - from.y));
|
||||
int w = from.width
|
||||
+ (int) (timelinePosition * (to.width - from.width));
|
||||
int h = from.height
|
||||
+ (int) (timelinePosition * (to.height - from.height));
|
||||
return new Rectangle(x, y, w, h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class getBasePropertyClass() {
|
||||
return Rectangle.class;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.swt;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.pushingpixels.trident.Timeline.TimelineState;
|
||||
import org.pushingpixels.trident.callback.RunOnUIThread;
|
||||
import org.pushingpixels.trident.callback.TimelineCallback;
|
||||
|
||||
@RunOnUIThread
|
||||
public class SWTRepaintCallback implements TimelineCallback {
|
||||
private Control control;
|
||||
|
||||
private Rectangle rect;
|
||||
|
||||
private AtomicBoolean repaintGuard;
|
||||
|
||||
public SWTRepaintCallback(Control control) {
|
||||
this(control, null);
|
||||
}
|
||||
|
||||
public SWTRepaintCallback(Control control, Rectangle rect) {
|
||||
if (control == null) {
|
||||
throw new NullPointerException("Control must be non-null");
|
||||
}
|
||||
this.control = control;
|
||||
if (rect != null) {
|
||||
this.rect = new Rectangle(rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void setAutoRepaintMode(boolean autoRepaintMode) {
|
||||
if (autoRepaintMode) {
|
||||
this.repaintGuard = null;
|
||||
} else {
|
||||
this.repaintGuard = new AtomicBoolean(false);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void forceRepaintOnNextPulse() {
|
||||
if (this.repaintGuard == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"This method cannot be called on auto-repaint callback");
|
||||
}
|
||||
this.repaintGuard.set(true);
|
||||
}
|
||||
|
||||
public synchronized void setRepaintRectangle(Rectangle rect) {
|
||||
if (rect == null) {
|
||||
this.rect = null;
|
||||
} else {
|
||||
this.rect = new Rectangle(rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelinePulse(float durationFraction, float timelinePosition) {
|
||||
redrawAsNecessary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimelineStateChanged(TimelineState oldState,
|
||||
TimelineState newState, float durationFraction,
|
||||
float timelinePosition) {
|
||||
redrawAsNecessary();
|
||||
}
|
||||
|
||||
private void redrawAsNecessary() {
|
||||
if (this.control.isDisposed())
|
||||
return;
|
||||
|
||||
if (this.repaintGuard != null) {
|
||||
if (!this.repaintGuard.compareAndSet(true, false)) {
|
||||
// no need to repaint
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.rect == null)
|
||||
this.control.redraw();
|
||||
else
|
||||
this.control.redraw(this.rect.x, this.rect.y, this.rect.width,
|
||||
this.rect.height, true);
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.swt;
|
||||
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.pushingpixels.trident.Timeline;
|
||||
|
||||
public class SWTRepaintTimeline extends Timeline {
|
||||
private SWTRepaintCallback repaintCallback;
|
||||
|
||||
public SWTRepaintTimeline(Control mainTimelineComp) {
|
||||
this(mainTimelineComp, null);
|
||||
}
|
||||
|
||||
public SWTRepaintTimeline(Control mainTimelineComp, Rectangle toRepaint) {
|
||||
super(mainTimelineComp);
|
||||
this.repaintCallback = new SWTRepaintCallback(mainTimelineComp,
|
||||
toRepaint);
|
||||
this.addCallback(this.repaintCallback);
|
||||
}
|
||||
|
||||
public void forceRepaintOnNextPulse() {
|
||||
this.repaintCallback.forceRepaintOnNextPulse();
|
||||
}
|
||||
|
||||
public void setAutoRepaintMode(boolean autoRepaintMode) {
|
||||
this.repaintCallback.setAutoRepaintMode(autoRepaintMode);
|
||||
}
|
||||
|
||||
public void setRepaintRectangle(Rectangle rect) {
|
||||
this.repaintCallback.setRepaintRectangle(rect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void play() {
|
||||
throw new UnsupportedOperationException(
|
||||
"Only infinite looping is supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playReverse() {
|
||||
throw new UnsupportedOperationException(
|
||||
"Only infinite looping is supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replay() {
|
||||
throw new UnsupportedOperationException(
|
||||
"Only infinite looping is supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replayReverse() {
|
||||
throw new UnsupportedOperationException(
|
||||
"Only infinite looping is supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playLoop(int loopCount, RepeatBehavior repeatBehavior) {
|
||||
if (loopCount >= 0) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Only infinite looping is supported");
|
||||
}
|
||||
super.playLoop(loopCount, repeatBehavior);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2005-2010 Trident Kirill Grouchnikov. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* o Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* o Neither the name of Trident Kirill Grouchnikov nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.pushingpixels.trident.swt;
|
||||
|
||||
import org.eclipse.swt.widgets.Widget;
|
||||
import org.pushingpixels.trident.UIToolkitHandler;
|
||||
|
||||
public class SWTToolkitHandler implements UIToolkitHandler {
|
||||
@Override
|
||||
public boolean isHandlerFor(Object mainTimelineObject) {
|
||||
return (mainTimelineObject instanceof Widget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInReadyState(Object mainTimelineObject) {
|
||||
return !((Widget) mainTimelineObject).isDisposed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runOnUIThread(Object mainTimelineObject, Runnable runnable) {
|
||||
((Widget) mainTimelineObject).getDisplay().asyncExec(runnable);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user