[trident~source:107] Wiki pages backup

  • From: kirillg@kenai.com
  • To: commits@trident.kenai.com
  • Subject: [trident~source:107] Wiki pages backup
  • Date: Mon, 8 Mar 2010 05:16:28 +0000

Project:    trident
Repository: source
Revision:   107
Author:     kirillg
Date:       2010-03-08 05:16:25 UTC
Link:       

Log Message:
------------
Wiki pages backup


Revisions:
----------
107


Added Paths:
------------
www/wiki-backup/KeyFrameOverview.wiki.txt
www/wiki-backup/SimpleSwingExample.wiki.txt
www/wiki-backup/ease-40.png
www/wiki-backup/ParallelSWTTimelines.wiki.txt
www/wiki-backup/UIToolkitSupport.wiki.txt
www/wiki-backup/TimelineOverview.wiki.txt
www/wiki-backup/SimpleTimelineScenario.wiki.txt
www/wiki-backup/CustomUIToolkitHandlers.wiki.txt
www/wiki-backup
www/wiki-backup/ParallelSwingTimelines.wiki.txt
www/wiki-backup/TimelineLifecycle.wiki.txt
www/wiki-backup/CustomPropertyInterpolators.wiki.txt
www/wiki-backup/SimpleSWTExample.wiki.txt
www/wiki-backup/ExtensionPoints.wiki.txt
www/wiki-backup/TimelineScenarioIntroduction.wiki.txt
www/wiki-backup/ease-80.png
www/wiki-backup/progressindication.png
www/wiki-backup/CustomPulseSource.wiki.txt
www/wiki-backup/Home.wiki.txt
www/wiki-backup/TimelineInterpolatingFields.wiki.txt
www/wiki-backup/KeyFrameExample.wiki.txt
www/wiki-backup/TimelineAdditionalConfiguration.wiki.txt


Diffs:
------
Index: www/wiki-backup/CustomUIToolkitHandlers.wiki.txt
===================================================================
--- www/wiki-backup/CustomUIToolkitHandlers.wiki.txt    (revision 0)
+++ www/wiki-backup/CustomUIToolkitHandlers.wiki.txt    (revision 107)
@@ -0,0 +1,84 @@
+== UI toolkit handlers ==
+
+Graphical applications are a natural fit for animations, and Trident core 
has built-in support for Swing and SWT. [[UIToolkitSupport|This support]] 
covers threading rules, custom property interpolators and repaint timelines. 
Application code that needs to support additional Java-based UI toolkits 
should register a custom UI toolkit handler. 
+
+Most modern UI toolkits have threading rules that the applications must 
respect in order to prevent application freeze and visual artifacts. The 
threading rules for both Swing and SWT specify that the UI-related operations 
must be done on a special UI thread, and the methods in the 
<code>org.pushingpixels.trident.UIToolkitHandler</code> are used to determine 
the relevance of these threading rules. The <code>UIToolkitHandler</code> 
interface is illustrated by the core support for SWT:
+
+<pre name="java">
+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(Runnable runnable) {
+               Display.getDefault().asyncExec(runnable);
+       }
+}
+</pre>
+
+This is a very simple implementation of a UI toolkit handler that respects 
the relevant threading rules:
+* The <code>isHandlerFor</code> associates this handler with all SWT widgets
+* The <code>isInReadyState</code> marks disposed widgets to skip the 
property interpolation / callback invocations
+* The <code>runOnUIThread</code> runs the UI related logic on the SWT thread
+
+== Registering custom UI toolkit handlers ==
+
+Trident provides two ways to register custom UI toolkit handlers - 
customization APIs and plugins. 
+
+The <code>TridentConfig</code> class has the following APIs to work with UI 
toolkit handlers: 
+
+* addUIToolkitHandler(UIToolkitHandler) - registers the UI toolkit handler
+* removeUIToolkitHandler(UIToolkitHandler) - unregisters the UI toolkit 
handler
+* getUIToolkitHandlers() - retrieves an unmodifiable collection of all 
registered (core and custom) UI toolkit handlers
+
+The '''UIToolkitHandler''' entries in the plugin descriptor files allow 
application code to support additional Java-based UI toolkits. The value 
associated with this key must be the fully qualified class name of an 
application class that implements the 
<code>org.pushingpixels.trident.UIToolkitHandler</code> interface. 
+
+== Respecting the threading rules ==
+
+The <code>UIToolkitHandler.isHandlerFor(Object)</code> is used to determine 
whether the main timeline object is a component / widget for the specific UI 
toolkit. At runtime, all fields registered with the 
<code>Timeline.addPropertyToInterpolate</code> methods will be changed on the 
UI thread using the <code>UIToolkitHandler.runOnUIThread</code> method.
+
+In the [[SimpleSwingExample|simple Swing example]] that interpolates the 
foreground color of a button on mouse rollover, the timeline is configured as
+
+<pre name="java">
+       Timeline rolloverTimeline = new Timeline(button);
+       rolloverTimeline.addPropertyToInterpolate("foreground", Color.blue,
+                       Color.red);
+</pre>
+
+If you put a breakpoint in the <code>JComponent.setForeground(Color)</code> 
- which is called on every timeline pulse - you will see that it is called on 
the Swing Event Dispatch Thread. Internally, this is what happens:
+
+* When the timeline is created, all registered UI toolkit handlers are asked 
whether they are handlers for the specified object
+* The <code>org.pushingpixels.trident.swing.SwingToolkitHandler</code> 
registered in the core library returns <code>true</code> for the button 
object in its <code>isHandlerFor(Object)</code>
+* On every timeline pulse, a <code>Runnable</code> object is created 
internally. The <code>run()</code> method calls the setters for all 
registered fields - using the <code>PropertyInterpolator.interpolate</code> 
method of the matching [[PropertyInterpolatorPlugin|property interpolator]]
+* This <code>Runnable</code> is passed to the 
<code>UIToolkitHandler.runOnUIThread</code> method of the matching UI toolkit 
handler.
+
+And this is how <code>SwingToolkitHandler.runOnUIThread()</code> is 
implemented:
+
+<pre name="java">
+       @Override
+       public void runOnUIThread(Runnable runnable) {
+               if (SwingUtilities.isEventDispatchThread())
+                       runnable.run();
+               else
+                       SwingUtilities.invokeLater(runnable);
+       }
+</pre>
+
+== Running custom application code on UI thread ==
+
+The flow described above works for the fields registered with the 
<code>Timeline.addPropertyToInterpolate</code> methods. What about the custom 
[[TimelineLifecycle|application callbacks]] registered with the 
<code>Timeline.addCallback()</code>? If the callback methods need to respect 
the UI threading rules of the matching toolkit, the 
<code>TimelineCallback</code> implementation class needs to be tagged with 
the <code>org.pushingpixels.trident.callback.RunOnUIThread</code> annotation. 
+
+Callback implementations marked with this annotation will have both 
<code>onTimelineStateChanged</code> and <code>onTimelinePulse</code> invoked 
on the UI thread, making it safe to query and change the UI. The 
<code>UIThreadTimelineCallbackAdapter</code> is a core adapter class that is 
marked with this annotation.
+
+== Querying the readiness of the timeline object ==
+
+The <code>isInReadyState(Object)</code> is the third and final method in the 
<code>UIToolkitHandler</code> interface. After the specific UI toolkit 
handler has declared that it will handle the main object of the specific 
timeline (by returning <code>true</code> from the 
<code>isHandlerFor(Object)</code> method), it will be used to interpolate the 
registered fields and run the registered callbacks. However, some UI toolkits 
may impose additional restrictions on when the UI object is ready to be 
queried / changed. 
+
+For example, once an SWT control is disposed, it will throw an 
<code>SWTException</code> in the <code>setForeground</code> method. So, if 
the application code is running a slow animation that changes the foreground 
color of a button, and the application window containing this button is 
disposed in the meantime, the call to <code>setForeground</code> should be 
skipped.
Index: www/wiki-backup/ease-40.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: www/wiki-backup/ease-40.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: www/wiki-backup/SimpleSWTExample.wiki.txt
===================================================================
--- www/wiki-backup/SimpleSWTExample.wiki.txt   (revision 0)
+++ www/wiki-backup/SimpleSWTExample.wiki.txt   (revision 107)
@@ -0,0 +1,68 @@
+== Interpolating foreground color of an SWT radio button ==
+The following example shows how to smoothly change the foreground color of 
an SWT radio button on mouse rollover. This example showcases a few Trident 
utilities that simplify animations on SWT controls.
+
+<pre name="java">
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.*;
+import org.pushingpixels.trident.Timeline;
+
+public class ButtonFg {
+       public static void main(String[] args) {
+               Display display = new Display();
+               Shell shell = new Shell(display);
+               shell.setSize(300, 200);
+               GridLayout layout = new GridLayout();
+               shell.setLayout(layout);
+
+               Button button = new Button(shell, SWT.RADIO);
+               GridData gridData = new GridData(GridData.CENTER, 
GridData.CENTER,
+                               true, false);
+               button.setLayoutData(gridData);
+
+               button.setText("sample");
+
+               Color blue = display.getSystemColor(SWT.COLOR_BLUE);
+               Color red = display.getSystemColor(SWT.COLOR_RED);
+               button.setForeground(blue);
+
+               final Timeline rolloverTimeline = new Timeline(button);
+               rolloverTimeline.addPropertyToInterpolate("foreground", blue, 
red);
+               rolloverTimeline.setDuration(2500);
+               button.addMouseTrackListener(new MouseTrackAdapter() {
+                       @Override
+                       public void mouseEnter(MouseEvent e) {
+                               rolloverTimeline.play();
+                       }
+
+                       @Override
+                       public void mouseExit(MouseEvent e) {
+                               rolloverTimeline.playReverse();
+                       }
+               });
+
+               shell.open();
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch())
+                               display.sleep();
+               }
+               display.dispose();
+       }
+}
+</pre>
+
+* Lines 18-21 create a <code>Button</code> and add it to the center of the 
frame (setting the <code>GridLayout</code>).
+* Line 27 sets the foreground of the radio button to blue
+* Line 29 creates a new timeline associated with this button.
+* Line 30 specifies that the timeline interpolates the button foreground 
color between blue and red.
+* Lines 32-42 add a mouse track listener to the button. When the mouse 
enters the button area, the timeline is played. When the mouse exits the 
button area, the timeline is played in reverse.
+
+This example shows how the <code>Control.setForeground(Color)</code> method 
is used together with the built in property interpolator for the 
<code>org.eclipse.swt.graphics.Color</code> class to run the timeline that 
interpolates the foreground color of an SWT radio button. Note that since the 
<code>Control.setForeground(Color)</code> also repaints the button, there is 
no need to explicitly repaint it on every timeline pulse.
+
+If you debug this application and put a breakpoint in the 
<code>Control.setForeground(Color)</code> method, you will see that it is 
called on the 
[http://help.eclipse.org/stable/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/swt_threading.htm
 SWT Thread]. This is a built-in capability of the Trident core. It 
recognizes that the timeline is associated with a SWT component, and calls 
the setter method (during the timeline pulses) on the SWT thread.
+
+Finally, since we are using the <code>Timeline.play()</code> and 
<code>Timeline.playReverse()</code> methods, the interpolation can be 
reversed in the middle if the user moves the mouse quickly. The rollover 
timeline in our example takes 2.5 seconds to complete. Suppose the user moves 
the mouse over the button, and then after one second moves the mouse away. 
The call to <code>playReverse</code> detects that this very timeline 
[[TimelineLifecycle|is already playing]], and starts playing it in reverse 
from its '''current''' position.
Index: www/wiki-backup/CustomPulseSource.wiki.txt
===================================================================
--- www/wiki-backup/CustomPulseSource.wiki.txt  (revision 0)
+++ www/wiki-backup/CustomPulseSource.wiki.txt  (revision 107)
@@ -0,0 +1,66 @@
+== Timeline pulse source ==
+By default, Trident timelines are driven by a dedicated thread that wakes up 
every 40ms and updates all the timelines. When the CPU is not heavily used 
this results in 25 frames-per-second refresh rate for Trident-driven UI 
animations - consistent with the frame rate of theatrical films and 
non-interlaced PAL television standard. 
+
+Applications that require custom pulse behavior - higher frame rate, lower 
frame rate or dynamic frame rate - should use the 
<code>TridentConfig.setPulseSource(PulseSource)</code> API. The 
<code>PulseSource</code> interface is:
+
+<pre name="java">
+public interface PulseSource {
+       public void waitUntilNextPulse();
+}
+</pre>
+
+The implementation of <code>waitUntilNextPulse()</code> is expected to be a 
blocking call that returns on the next target pulse.
+
+== Sample usage ==
+
+The following class installs a custom pulse source that fires timeline 
pulses every 100 milliseconds:
+
+<pre name="java">
+public class CustomPulseSource {
+   private float value;
+
+   public void setValue(float newValue) {
+      SimpleDateFormat sdf = new SimpleDateFormat("mm:SSS");
+      System.out.println(sdf.format(new Date()) + " : " + this.value + " -> "
+            + newValue);
+      this.value = newValue;
+   }
+
+   public static void main(String[] args) {
+      TridentConfig.getInstance().setPulseSource(
+            new TridentConfig.PulseSource() {
+               @Override
+               public void waitUntilNextPulse() {
+                  try {
+                     Thread.sleep(100);
+                  } catch (InterruptedException ie) {
+                     ie.printStackTrace();
+                  }
+               }
+            });
+      CustomPulseSource helloWorld = new CustomPulseSource();
+      Timeline timeline = new Timeline(helloWorld);
+      timeline.addPropertyToInterpolate("value", 0.0f, 1.0f);
+      timeline.play();
+
+      try {
+         Thread.sleep(3000);
+      } catch (Exception exc) {
+      }
+   }
+}
+</pre>
+
+This example uses the blocking <code>Thread.sleep()</code> API. Sample 
output of running this class is:
+
+<pre>
+40:362 : 0.0 -> 0.0
+40:362 : 0.0 -> 0.0
+40:456 : 0.0 -> 0.2
+40:550 : 0.2 -> 0.746
+40:659 : 0.746 -> 0.946
+40:753 : 0.946 -> 1.0
+40:753 : 1.0 -> 1.0
+</pre>
+
+Discarding the first and last lines - that correspond to the start and the 
end of the timeline - the custom pulse source is indeed firing on average 
every 100 ms as expected. Applications that have a higher-precision timer 
implementation - perhaps using native APIs - can provide the matching 
<code>PulseSource</code> implementation to achieve a higher pulse rate.
\ No newline at end of file
Index: www/wiki-backup/ParallelSWTTimelines.wiki.txt
===================================================================
--- www/wiki-backup/ParallelSWTTimelines.wiki.txt       (revision 0)
+++ www/wiki-backup/ParallelSWTTimelines.wiki.txt       (revision 107)
@@ -0,0 +1,149 @@
+== Multiple timelines in SWT applications ==
+
+Trident supports running multiple independent timelines at the same time. 
This page shows the SWT application behind [http://vimeo.com/3404285 this ;
video], where every cell rollover is implemented as a separate timeline.
+
+We start with a class that implements a specific grid cell:
+
+<pre name="java">
+       public static class SnakePanelRectangle {
+               private Color backgroundColor;
+
+               private boolean isRollover;
+
+               private Timeline rolloverTimeline;
+
+               public SnakePanelRectangle() {
+                       this.backgroundColor = 
Display.getDefault().getSystemColor(
+                                       SWT.COLOR_BLACK);
+                       this.isRollover = false;
+
+                       this.rolloverTimeline = new Timeline(this);
+                       
this.rolloverTimeline.addPropertyToInterpolate("backgroundColor",
+                                       
Display.getDefault().getSystemColor(SWT.COLOR_YELLOW),
+                                       
Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+                       this.rolloverTimeline.setDuration(2500);
+               }
+
+               public void setRollover(boolean isRollover) {
+                       if (this.isRollover == isRollover)
+                               return;
+                       this.isRollover = isRollover;
+                       if (this.isRollover) {
+                               this.rolloverTimeline.replay();
+                       }
+               }
+
+               public void setBackgroundColor(Color backgroundColor) {
+                       this.backgroundColor = backgroundColor;
+               }
+
+               public Color getBackgroundColor() {
+                       return backgroundColor;
+               }
+       }
+</pre>
+
+A few major points in this class:
+* The default background color of a cell is black (lines 9-10). 
+* The rollover timeline interpolates the background color from yellow to 
black over a period of 2.5 seconds (lines 13-17).
+* The rollover timeline is replayed when <code>setRollover</code> is called 
with <code>true</code>. This restarts the timeline to interpolate the 
foreground color from yellow.
+
+The next class implements a cell grid, tracing the mouse events and 
dispatching the rollover events to the relevant cells:
+
+<pre name="java">
+       private static class SnakePanel extends Canvas {
+
+               private SnakePanelRectangle[][] grid;
+
+               private int ROWS = 10;
+
+               private int COLUMNS = 20;
+
+               private int DIM = 20;
+
+               public SnakePanel(Composite parent) {
+                       super(parent, SWT.DOUBLE_BUFFERED);
+
+                       this.grid = new SnakePanelRectangle[COLUMNS][ROWS];
+                       for (int i = 0; i < COLUMNS; i++) {
+                               for (int j = 0; j < ROWS; j++) {
+                                       this.grid[i][j] = new 
SnakePanelRectangle();
+                               }
+                       }
+
+                       Timeline repaint = new SWTRepaintTimeline(this);
+                       repaint.playLoop(RepeatBehavior.LOOP);
+
+                       this.addMouseMoveListener(new MouseMoveListener() {
+                               int rowOld = -1;
+                               int colOld = -1;
+
+                               @Override
+                               public void mouseMove(MouseEvent e) {
+                                       int x = e.x;
+                                       int y = e.y;
+
+                                       int column = x / (DIM + 1);
+                                       int row = y / (DIM + 1);
+
+                                       if ((column >= COLUMNS) || (row >= 
ROWS))
+                                               return;
+
+                                       if ((column != colOld) || (row != 
rowOld)) {
+                                               if ((colOld >= 0) && (rowOld 
>= 0))
+                                                       
grid[colOld][rowOld].setRollover(false);
+                                               
grid[column][row].setRollover(true);
+                                       }
+                                       colOld = column;
+                                       rowOld = row;
+                               }
+                       });
+
+                       this.addPaintListener(new PaintListener() {
+                               @Override
+                               public void paintControl(PaintEvent e) {
+                                       GC gc = e.gc;
+                                       
gc.setBackground(e.display.getSystemColor(SWT.COLOR_BLACK));
+                                       gc.fillRectangle(e.x, e.y, e.width, 
e.height);
+
+                                       for (int i = 0; i < COLUMNS; i++) {
+                                               for (int j = 0; j < ROWS; 
j++) {
+                                                       SnakePanelRectangle 
rect = grid[i][j];
+                                                       Color backgr = 
rect.getBackgroundColor();
+                                                       
gc.setBackground(backgr);
+                                                       gc.fillRectangle(i * 
(DIM + 1), j * (DIM + 1), DIM,
+                                                                       DIM);
+                                               }
+                                       }
+                               }
+                       });
+               }
+       }
+</pre>
+
+A few major points in this class:
+* A special type of timeline is created and played in a loop - lines 21-22. 
In this example, each cell rollover timeline changes the background color of 
that cell, but '''does not''' cause the repaint. Instead, we have a "master" 
repaint timeline that runs in a loop and causes the repaint of the entire 
grid panel.
+* The mouse move listener (lines 24-47) tracks the mouse location, calling 
the <code>setRollover</code> method on relevant cells. Since each cell 
rollover timeline runs for 2.5 seconds, quick mouse moves will result in 
multiple timelines running in parallel.
+* The painting of each cell respects the current background color of that 
cell - lines 59-62.
+
+Finally, the main method that creates a host frame and adds the cell grid 
panel to it:
+
+<pre name="java">
+       public static void main(String[] args) {
+               Display display = new Display();
+               Shell shell = new Shell(display);
+               shell.setSize(430, 240);
+               shell.setText("SWT Snake");
+               FillLayout layout = new FillLayout();
+               shell.setLayout(layout);
+
+               SnakePanel snake = new SnakePanel(shell);
+
+               shell.open();
+               while (!shell.isDisposed()) {
+                       if (!display.readAndDispatch())
+                               display.sleep();
+               }
+               display.dispose();
+       }
+</pre>
Index: www/wiki-backup/SimpleTimelineScenario.wiki.txt
===================================================================
--- www/wiki-backup/SimpleTimelineScenario.wiki.txt     (revision 0)
+++ www/wiki-backup/SimpleTimelineScenario.wiki.txt     (revision 107)
@@ -0,0 +1,303 @@
+== Simple Swing timeline scenario ==
+
+The following example shows a Swing application with a simple 
[[TimelineScenarioIntroduction|timeline scenario]] that launches five 
parallel timelines. It shows the code behind [http://vimeo.com/3401957 this ;
video], where every volley is a separate timeline, and all currently playing 
volleys are part of the same timeline scenario.
+
+In the code, there are three "hierarchy" levels of fireworks:
+* The entire fireworks display - this is a timeline scenario that consists 
of five volley explosions.
+* The volley explosion implemented in <code>VolleyExplosion</code> class - 
this is a collection of single explosions that have the same color and 
originate from the same explosion center point.
+* The single explosion implemented in <code>SingleExplosion</code> class - 
this is a fading circle that represents a single "leaf" part of the volley 
explosion.
+
+The code behind the single explosion is quite simple:
+
+<pre name="java">
+       public class SingleExplosion {
+               float x;
+
+               float y;
+
+               float radius;
+
+               float opacity;
+
+               Color color;
+
+               public SingleExplosion(Color color, float x, float y, float 
radius) {
+                       this.color = color;
+                       this.x = x;
+                       this.y = y;
+                       this.radius = radius;
+                       this.opacity = 1.0f;
+               }
+
+               public void setX(float x) {
+                       this.x = x;
+               }
+
+               public void setY(float y) {
+                       this.y = y;
+               }
+
+               public void setRadius(float radius) {
+                       this.radius = radius;
+               }
+
+               public void setOpacity(float opacity) {
+                       this.opacity = opacity;
+               }
+
+               public void paint(Graphics g) {
+                       Graphics2D g2d = (Graphics2D) g.create();
+                       g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                       RenderingHints.VALUE_ANTIALIAS_ON);
+                       
g2d.setComposite(AlphaComposite.SrcOver.derive(this.opacity));
+                       g2d.setColor(this.color);
+                       g2d.fill(new Ellipse2D.Float(this.x - this.radius, 
this.y
+                                       - this.radius, 2 * radius, 2 * 
radius));
+                       g2d.dispose();
+               }
+       }
+</pre>
+
+It has four fields (lines 2-10) that specify its location, size, opacity and 
color. Each field except the color has a public setter that is used in the 
timeline created in the parent volley explosion (lines 20-34). Finally, it 
has a custom painting implementation that paints the graphical representation 
of the single volley (lines 36-45).
+
+The volley explosion is implemented by the following class:
+<pre name="java">
+       public class VolleyExplosion {
+               private int x;
+
+               private int y;
+
+               private Color color;
+
+               private Set<SingleExplosion> circles;
+
+               public VolleyExplosion(int x, int y, Color color) {
+                       this.x = x;
+                       this.y = y;
+                       this.color = color;
+                       this.circles = new HashSet<SingleExplosion>();
+               }
+
+               public TimelineScenario getExplosionScenario() {
+                       TimelineScenario scenario = new 
TimelineScenario.Parallel();
+
+                       int duration = 1000 + (int) (1000 * Math.random());
+                       for (int i = 0; i < 18; i++) {
+                               float dist = (float) (50 + 10 * 
Math.random());
+                               float radius = (float) (2 + 2 * 
Math.random());
+                               for (float delta = 0.6f; delta <= 1.0f; delta 
+= 0.2f) {
+                                       float circleRadius = radius * delta;
+
+                                       double degrees = 20.0 * (i + 
Math.random());
+                                       float radians = (float) (2.0 * 
Math.PI * degrees / 360.0);
+
+                                       float initDist = delta * dist / 10.0f;
+                                       float finalDist = delta * dist;
+                                       float initX = (float) (this.x + 
initDist
+                                                       * Math.cos(radians));
+                                       float initY = (float) (this.y + 
initDist
+                                                       * Math.sin(radians));
+                                       float finalX = (float) (this.x + 
finalDist
+                                                       * Math.cos(radians));
+                                       float finalY = (float) (this.y + 
finalDist
+                                                       * Math.sin(radians));
+
+                                       SingleExplosion circle = new 
SingleExplosion(this.color,
+                                                       initX, initY, 
circleRadius);
+                                       Timeline timeline = new 
Timeline(circle);
+                                       
timeline.addPropertyToInterpolate("x", initX, finalX);
+                                       
timeline.addPropertyToInterpolate("y", initY, finalY);
+                                       
timeline.addPropertyToInterpolate("opacity", 1.0f, 0.0f);
+                                       timeline.setDuration(duration - 200
+                                                       + (int) (400 * 
Math.random()));
+                                       timeline.setEase(new Spline(0.4f));
+
+                                       synchronized (this.circles) {
+                                               circles.add(circle);
+                                       }
+                                       scenario.addScenarioActor(timeline);
+                               }
+                       }
+
+                       return scenario;
+               }
+
+               public void paint(Graphics g) {
+                       synchronized (this.circles) {
+                               for (SingleExplosion circle : this.circles) {
+                                       circle.paint(g);
+                               }
+                       }
+               }
+       }
+</pre>
+
+A quick walkthrough:
+* Lines 2-6 specify the fields that store the center and the color of the 
explosion.
+* Line 8 specifies the set that stores all the single explosions of this 
volley explosion.
+* Lines 17-59 return a timeline scenario that implements this volley 
explosion:
+** Line 18 creates a parallel timeline. '''Each''' single explosion is 
implemented as a separate timeline.
+** Line 20 computes a random duration for this scenario.
+** Lines 21 and 24 create 54 single explosions. 
+** Lines 21, 27 and 28 create single explosions at almost evenly distributed 
angles (every 20 degrees).
+** Lines 22, 24 and 30-39 create single explosions at almost evenly 
distributed distance from the center (three for each angle).
+** Lines 41-42 create the <code>SingleExplosion</code> object.
+** Lines 43-49 create the timeline that interpolates the relevant properties 
of that object. Each timeline has a random duration.
+** Line 54 adds the created timeline to the scenario.
+* The scenario returned on line 58 has 54 different timelines, one for each 
single explosion.
+* Lines 61-67 paint all the single explosions of this volley explosion.
+
+Now we get to the main application class. It implements the following 
functionality:
+* Playing five explosion volleys (five timeline scenarios).
+* Waiting for all five to be done.
+* Playing another five - repeating the previous two steps.
+* Listening to the mouse events, suspending the currently playing scenarios 
on mouse press, and resuming them on mouse release.
+
+The code starts by declaring the relevant data structures:
+<pre name="java">
+public class Fireworks extends JFrame {
+       private Set<VolleyExplosion> volleys;
+
+       private Map<VolleyExplosion, TimelineScenario> volleyScenarios;
+
+       private JPanel mainPanel;
+</pre>
+
+Here is the constructor of this class:
+<pre name="java">
+public Fireworks() {
+   super("Swing Fireworks");
+
+   this.mainPanel = new JPanel() {
+      @Override
+      protected void paintComponent(Graphics g) {
+         super.paintComponent(g);
+         synchronized (volleys) {
+            for (VolleyExplosion exp : volleys)
+               exp.paint(g);
+         }
+      }
+   };
+   this.mainPanel.setBackground(Color.black);
+   this.mainPanel.setPreferredSize(new Dimension(480, 320));
+
+   Timeline repaint = new SwingRepaintTimeline(this);
+   repaint.playLoop(RepeatBehavior.LOOP);
+
+   this.volleys = new HashSet<VolleyExplosion>();
+   this.volleyScenarios = new HashMap<VolleyExplosion, TimelineScenario>();
+
+   this.mainPanel.addMouseListener(new MouseAdapter() {
+      @Override
+      public void mousePressed(MouseEvent e) {
+         synchronized (volleys) {
+            for (TimelineScenario scenario : volleyScenarios.values())
+               scenario.suspend();
+         }
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent e) {
+         synchronized (volleys) {
+            for (TimelineScenario scenario : volleyScenarios.values())
+               scenario.resume();
+         }
+      }
+   });
+
+   mainPanel.addComponentListener(new ComponentAdapter() {
+      @Override
+      public void componentResized(ComponentEvent e) {
+         if ((mainPanel.getWidth() == 0) || (mainPanel.getHeight() == 0))
+            return;
+         new Thread() {
+            @Override
+            public void run() {
+               while (true) {
+                  addExplosions(5);
+               }
+            }
+         }.start();
+      }
+   });
+
+   this.add(mainPanel);
+   this.pack();
+   this.setLocationRelativeTo(null);
+   this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+}
+</pre>
+
+* Lines 4-15 create a <code>JPanel</code> that paints all currently playing 
volley explosions on black background.
+* Lines 17-18 create a looping timeline that repaints the contents of this 
application.
+* Lines 20-21 create the data structures tracking the currently playing 
explosions.
+* Lines 23-39 add the mouse listener that suspends the currently playing 
scenarios on mouse press and resumes them on mouse release. 
+* Lines 41-55 register a listener that checks the size of the panel. When 
the panel has a non-zero size, the listener creates and runs a thread that 
adds five explosions in an infinite loop (see the explanation of 
<code>addExplosions</code> below)
+* Lines 57-60 configure the application hierarchy and location
+
+Here is the method that makes sure that the volley explosions are run in 
batches of 5, even when they have different durations:
+
+<pre name="java">
+       private void addExplosions(int count) {
+               final CountDownLatch latch = new CountDownLatch(count);
+
+               for (int i = 0; i < count; i++) {
+                       int r = (int) (255 * Math.random());
+                       int g = (int) (100 + 155 * Math.random());
+                       int b = (int) (50 + 205 * Math.random());
+                       Color color = new Color(r, g, b);
+
+                       int x = 60 + (int) ((mainPanel.getWidth() - 120) * 
Math.random());
+                       int y = 60 + (int) ((mainPanel.getHeight() - 120) * 
Math.random());
+                       final VolleyExplosion exp = new VolleyExplosion(x, y, 
color);
+                       synchronized (volleys) {
+                               volleys.add(exp);
+                               TimelineScenario scenario = 
exp.getExplosionScenario();
+                               scenario.addCallback(new 
TimelineScenarioCallback() {
+                                       @Override
+                                       public void onTimelineScenarioDone() {
+                                               synchronized (volleys) {
+                                                       volleys.remove(exp);
+                                                       
volleyScenarios.remove(exp);
+                                                       latch.countDown();
+                                               }
+                                       }
+                               });
+                               volleyScenarios.put(exp, scenario);
+                               scenario.play();
+                       }
+               }
+
+               try {
+                       latch.await();
+               } catch (Exception exc) {
+               }
+       }
+</pre>
+
+* Line 2 creates a <code>CountDownLatch</code> that will be used to wait 
until all timeline scenarios that run the volley explosions are done
+* Lines 5-8 compute a random color for each one of the volley explosions
+* Lines 10-11 compute a random center location for each one of the volley 
explosions
+* Line 12 creates a new <code>VolleyExplosion</code> object
+* Line 15 creates the timeline scenario that corresponds to this object
+* Lines 16-25 add a callback that notifies the count down latch when the 
timeline scenario is done
+* Line 27 plays the timeline scenario
+* Lines 31-34 wait on the count down latch - until '''all''' timeline 
scenarios are done
+
+And finally, the main method to launch the fireworks:
+
+<pre name="java">
+
+       public static void main(String[] args) {
+               SwingUtilities.invokeLater(new Runnable() {
+                       @Override
+                       public void run() {
+                               new Fireworks().setVisible(true);
+                       }
+               });
+       }
+</pre>
+
+----
+<center>Click below for the WebStart demo</center>
+<center><a 
href="https://kenai.com/svn/trident~source/www/webstart/Fireworks.jnlp";>[[image:webstart.png]]</a></center>
Index: www/wiki-backup/ease-80.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: www/wiki-backup/ease-80.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: www/wiki-backup/CustomPropertyInterpolators.wiki.txt
===================================================================
--- www/wiki-backup/CustomPropertyInterpolators.wiki.txt        (revision 0)
+++ www/wiki-backup/CustomPropertyInterpolators.wiki.txt        (revision 107)
@@ -0,0 +1,185 @@
+== Property interpolators ==
+
+Trident supports interpolation of primitive values - such as integers, 
floats and point / color / rectangle classes of supported 
[[UIToolkitSupport|UI toolkits]]. Application code that needs to interpolate 
fields of these types does not need to explicitly state how the field value 
is interpolated between the start / current and end value. For other field 
types the application code can either register custom property interpolators, 
or [[TimelineInterpolatingFields|explicitly state]] the property interpolator 
to be used for computing the field value.
+
+For both cases the application code needs to provide one or more 
implementations of the 
<code>org.pushingpixels.trident.interpolator.PropertyInterpolator</code> 
interface. This interface has two methods. 
+
+The <code>public Class getBasePropertyClass()</code> is used to choose the 
property interpolator in the <code>Timeline.addPropertyToInterpolate(String, 
Object, Object)</code>. Internally, all registered property interpolators are 
queried to check whether they support the specified ''from'' and ''to'' 
values using the <code>Class.isAssignableFrom(Class)</code>. The '''first''' 
property interpolator that has a match for both values will be used. 
+
+For example, the <code>PointInterpolator</code> in the core AWT property 
interpolator source (<code>AWTPropertyInterpolators</code> class) has the 
following implementation of this method:
+
+<pre name="java">
+       @Override
+       public Class getBasePropertyClass() {
+               return Point.class;
+       }
+</pre>
+
+The <code>public T interpolate(T from, T to, float timelinePosition)</code> 
is used to compute the interpolated value during the current timeline pulse. 
For example, the <code>PointInterpolator</code> in the core AWT property 
interpolator source (<code>AWTPropertyInterpolators</code> class) has the 
following implementation of this method:
+
+<pre name="java">
+       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);
+       }
+</pre>
+
+== Registering custom property interpolators ==
+
+<code>TimelinePropertyBuilder.interpolatedWith(PropertyInterpolator)</code> 
API can be used to explicitly state the property interpolator to be used for 
the specific property. However, using this API may lead to a lot of 
boilerplate code in applications that have multiple animations of fields of 
the same custom type. In such cases it is recommended to register custom 
property interpolators and have Trident automatically pick up the matching 
interpolator at runtime. Trident provides two ways to register custom 
interpolators - customization APIs and plugins.
+
+The <code>TridentConfig</code> class has the following APIs to work with 
property interpolators:
+
+* <code>addPropertyInterpolatorSource(PropertyInterpolatorSource)</code> - 
registers all the property interpolators provided by this source
+* <code>addPropertyInterpolator(PropertyInterpolator)</code> - registers the 
property interpolator
+* <code>removePropertyInterpolator(PropertyInterpolator)</code> - 
unregisters the property interpolator
+* <code>getPropertyInterpolators()</code> - retrieves an unmodifiable list 
of all registered (core and custom) property interpolators
+* <code>getPropertyInterpolator(Object...)</code> - retrieves the first 
property interpolator that matches all the passed objects, or 
<code>null</code> if no match is found
+
+The '''PropertyInterpolatorSource''' entries in the [[PluginOverview|plugin 
descriptor]] files allow application code to provide property interpolators 
for custom application classes. The value associated with this key must be 
the fully qualified class name of an application class that implements the 
<code>org.pushingpixels.trident.interpolator.PropertyInterpolatorSource</code>
 interface.
+
+This interface has one method - <code>public Set&lt;PropertyInterpolator&gt; 
getPropertyInterpolators()</code> - which returns a set of custom property 
interpolators. Custom property interpolators can be used in two ways:
+
+* The <code>Timeline.addPropertyToInterpolate(String, Object, Object)</code> 
API that will choose the property interpolator that matches the types of the 
''from'' and ''to'' values
+* Use a [[TimelineInterpolatingFields|timeline property builder]] and the 
<code>TimelinePropertyBuilder.interpolatedWith()</code> API. The 
<code>Timeline.addPropertyToInterpolate(TimelinePropertyBuilder)</code> API 
will use the specified property interpolator
+
+== Bringing it together ==
+
+Let's look at the following Swing example that moves a red rectangle in an 
infinite loop:
+
+<pre name="java">
+public class MovingRectangle extends JFrame {
+   public static class MyRectangle {
+      private Point corner = new Point(0, 0);
+
+      public void setCorner(Point corner) {
+         this.corner = corner;
+      }
+
+      void paint(Graphics g) {
+         g.setColor(Color.red);
+         g.fillRect(this.corner.x, this.corner.y, 100, 100);
+      }
+   }
+
+   public MovingRectangle() {
+      super("Moving rectangle");
+      final MyRectangle rectangle = new MyRectangle();
+      JPanel panel = new JPanel() {
+         @Override
+         protected void paintComponent(Graphics g) {
+            super.paintComponent(g);
+            rectangle.paint(g);
+         }
+      };
+
+      Timeline move = new Timeline(rectangle);
+      move.addPropertyToInterpolate("corner", new Point(0, 0), new Point(100,
+            80));
+      move.setDuration(2000);
+      move.playLoop(RepeatBehavior.REVERSE);
+
+      Timeline repaint = new SwingRepaintTimeline(panel);
+      repaint.playLoop(RepeatBehavior.LOOP);
+
+      this.add(panel);
+      this.setSize(200, 200);
+      this.setLocationRelativeTo(null);
+      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+   }
+
+   public static void main(String[] args) {
+      SwingUtilities.invokeLater(new Runnable() {
+         @Override
+         public void run() {
+            new MovingRectangle().setVisible(true);
+         }
+      });
+   }
+}
+</pre>
+
+What happens in lines 35-36? Internally, the Trident core looks at all 
available property interpolators and finds that the 
<code>AWTPropertyInterpolators.PointInterpolator</code> is the best match for 
the passed values (which are both <code>java.awt.Point</code>s). Then, at 
every pulse of the <code>move</code> timeline, the 
<code>MyRectangle.setCorner(Point)</code> is called. 
+
+Note that the application code '''did not''' explicitly specify which 
property interpolator should be used. The following class illustrates the 
usage of <code>TridentConfig.addPropertyInterpolator</code> API:
+
+<pre name="java">
+public class CustomPropertyInterpolatorSource extends JFrame {
+   private Ellipse2D ellipse;
+
+   private static class Ellipse2DPropertyInterpolator implements
+         PropertyInterpolator<Ellipse2D> {
+      public Class getBasePropertyClass() {
+         return Ellipse2D.class;
+      }
+
+      @Override
+      public Ellipse2D interpolate(Ellipse2D from, Ellipse2D to,
+            float timelinePosition) {
+         double x = from.getX() + timelinePosition
+               * (to.getX() - from.getX());
+         double y = from.getY() + timelinePosition
+               * (to.getY() - from.getY());
+         double w = from.getWidth() + timelinePosition
+               * (to.getWidth() - from.getWidth());
+         double h = from.getHeight() + timelinePosition
+               * (to.getHeight() - from.getHeight());
+         return new Ellipse2D.Double(x, y, w, h);
+      }
+   }
+
+   public CustomPropertyInterpolatorSource() {
+      TridentConfig.getInstance().addPropertyInterpolator(
+            new Ellipse2DPropertyInterpolator());
+
+      Ellipse2D from = new Ellipse2D.Double(10, 10, 100, 50);
+      Ellipse2D to = new Ellipse2D.Double(40, 40, 200, 120);
+      this.ellipse = (Ellipse2D) from.clone();
+      JPanel ellipsePanel = new JPanel() {
+         @Override
+         protected void paintComponent(Graphics g) {
+            super.paintComponent(g);
+
+            Graphics2D g2d = (Graphics2D) g.create();
+            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                  RenderingHints.VALUE_ANTIALIAS_ON);
+            g2d.setColor(Color.red);
+            g2d.fill(ellipse);
+
+            g2d.dispose();
+         }
+      };
+      ellipsePanel.setBackground(Color.black);
+
+      Timeline ellipseTimeline = new Timeline(this);
+      ellipseTimeline.addPropertyToInterpolate("ellipse", from, to);
+      ellipseTimeline.setEase(new Sine());
+      ellipseTimeline.setDuration(2000);
+      ellipseTimeline.playLoop(RepeatBehavior.REVERSE);
+
+      new SwingRepaintTimeline(ellipsePanel).playLoop(RepeatBehavior.LOOP);
+
+      this.add(ellipsePanel);
+
+      this.setSize(400, 300);
+      this.setLocationRelativeTo(null);
+      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+   }
+
+   public void setEllipse(Ellipse2D ellipse) {
+      this.ellipse = ellipse;
+   }
+
+   public static void main(String[] args) {
+      SwingUtilities.invokeLater(new Runnable() {
+         @Override
+         public void run() {
+            new CustomPropertyInterpolatorSource().setVisible(true);
+         }
+      });
+   }
+}
+</pre>
+
+Here, the registered property interpolator is implicitly used to animate the 
expanding / shrinking ellipse.
\ No newline at end of file
Index: www/wiki-backup/progressindication.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: www/wiki-backup/progressindication.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Index: www/wiki-backup/ExtensionPoints.wiki.txt
===================================================================
--- www/wiki-backup/ExtensionPoints.wiki.txt    (revision 0)
+++ www/wiki-backup/ExtensionPoints.wiki.txt    (revision 107)
@@ -0,0 +1,47 @@
+== Core functionality ==
+The core functionality of the Trident library can be extended to address 
custom needs of the specific applications. Out of the box Trident supports:
+
+* The timeline pulses being fired by a dedicated thread that wakes up every 
40ms and updates all the timelines
+* Interpolating [[TimelineInterpolatingFields|float and integer fields]] of 
any Java object that provides the matching public setter methods.
+* Swing and SWT [[UIToolkitSupport|UI toolkits]], respecting the threading 
rules and providing interpolators for the custom graphic classes
+
+Interested applications can use Trident plugins and Trident configuration 
APIs to:
+
+* Provide a custom pulse source to drive the timeline updates
+* Provide additional property interpolators for custom application classes
+* Support additional Java-based UI toolkits
+
+== Configuration APIs ==
+
+The <code>org.pushingpixels.trident.TridentConfig</code> class contains the 
published configuration APIs. The <code>TridentConfig.getInstance()</code> 
API retrieves an instance of this class which can then be used for:
+
+* Setting a custom [[CustomPulseSource|pulse source]]
+* Adding, removing and querying [[CustomPropertyInterpolators|property 
interpolators]]
+* Adding, removing and querying [[CustomUIToolkitHandlers|UI toolkit 
handlers]]
+
+== Plugins ==
+
+A Trident plugin is specified by the 
<code>META-INF/trident-plugin.properties</code> file that should be placed in 
the runtime classpath. Note that you can have multiple plugins in the same 
runtime environment - if each one is coming from a different classpath jar, 
for example. 
+
+The format of <code>trident-plugin.properties</code> is simple. Each line in 
this file should be of the following format:
+<pre>
+Key=FullyQualifiedClassName
+</pre>
+
+There supported keys are:
+
+* '''PropertyInterpolatorSource''' allows specifying 
[[CustomPropertyInterpolators|custom property interpolators]]
+* '''UIToolkitHandler''' allows supporting [[CustomUIToolkitHandlers|custom 
UI toolkits]]
+
+The core Trident library contains a plugin that supports Swing and SWT UI 
toolkits, as well as property interpolators for a few core classes. The 
plugin descriptor is <code>META-INF/trident-plugin.properties</code> in the 
<code>trident.jar</code>
+
+<pre>
+UIToolkitHandler=org.pushingpixels.trident.swing.SwingToolkitHandler
+PropertyInterpolatorSource=org.pushingpixels.trident.swing.AWTPropertyInterpolators
+
+UIToolkitHandler=org.pushingpixels.trident.swt.SWTToolkitHandler
+PropertyInterpolatorSource=org.pushingpixels.trident.swt.SWTPropertyInterpolators
+
+PropertyInterpolatorSource=org.pushingpixels.trident.interpolator.CorePropertyInterpolators
+</pre>
+
Index: www/wiki-backup/SimpleSwingExample.wiki.txt
===================================================================
--- www/wiki-backup/SimpleSwingExample.wiki.txt (revision 0)
+++ www/wiki-backup/SimpleSwingExample.wiki.txt (revision 107)
@@ -0,0 +1,67 @@
+== Interpolating foreground color of a Swing button ==
+The following example shows how to smoothly change the foreground color of a 
Swing button on mouse rollover. This example showcases a few Trident 
utilities that simplify animations on Swing controls.
+
+<pre name="java">
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.*;
+
+import org.pushingpixels.trident.Timeline;
+
+public class ButtonFg extends JFrame {
+       public ButtonFg() {
+               JButton button = new JButton("sample");
+               button.setForeground(Color.blue);
+
+               this.setLayout(new FlowLayout());
+               this.add(button);
+
+               final Timeline rolloverTimeline = new Timeline(button);
+               rolloverTimeline.addPropertyToInterpolate("foreground", 
Color.blue,
+                               Color.red);
+               rolloverTimeline.setDuration(2500);
+               button.addMouseListener(new MouseAdapter() {
+                       @Override
+                       public void mouseEntered(MouseEvent e) {
+                               rolloverTimeline.play();
+                       }
+
+                       @Override
+                       public void mouseExited(MouseEvent e) {
+                               rolloverTimeline.playReverse();
+                       }
+               });
+
+               this.setSize(400, 200);
+               this.setLocationRelativeTo(null);
+               this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+       }
+
+       public static void main(String[] args) {
+               SwingUtilities.invokeLater(new Runnable() {
+                       @Override
+                       public void run() {
+                               new ButtonFg().setVisible(true);
+                       }
+               });
+       }
+}
+</pre>
+
+* Lines 12-16 create a <code>JButton</code> with blue foreground color and 
add it to the center of the frame (setting the <code>FlowLayout</code>).
+* Line 18 creates a new timeline associated with this button.
+* Lines 19-20 specify that the timeline interpolates the button foreground 
color between blue and red.
+* Lines 22-32 add a mouse listener to the button. When the mouse enters the 
button area, the timeline is played. When the mouse exits the button area, 
the timeline is played in reverse.
+
+This example shows how the <code>JComponent.setForeground(Color)</code> 
method is used together with the built in property interpolator for the 
<code>java.awt.Color</code> class to run the timeline that interpolates the 
foreground color of a Swing button. Note that since the 
<code>JComponent.setForeground(Color)</code> also repaints the button, there 
is no need to explicitly repaint it on every timeline pulse.
+
+If you debug this application and put a breakpoint in the 
<code>JComponent.setForeground(Color)</code> method, you will see that it is 
called on the 
[http://www.javaworld.com/javaworld/jw-08-2007/jw-08-swingthreading.html ;
Event Dispatch Thread]. This is a built-in capability of the Trident core. It 
recognizes that the timeline is associated with a Swing component, and calls 
the setter method (during the timeline pulses) on the EDT.
+
+Finally, since we are using the <code>Timeline.play()</code> and 
<code>Timeline.playReverse()</code> methods, the interpolation can be 
reversed in the middle if the user moves the mouse quickly. The rollover 
timeline in our example takes 2.5 seconds to complete. Suppose the user moves 
the mouse over the button, and then after one second moves the mouse away. 
The call to <code>playReverse</code> detects that this very timeline 
[[TimelineLifecycle|is already playing]], and starts playing it in reverse 
from its '''current''' position.
+
+----
+<center>Click below for the WebStart demo</center>
+<center><a 
href="https://kenai.com/svn/trident~source/www/webstart/ButtonFg.jnlp";>[[image:webstart.png]]</a></center>
\ No newline at end of file
Index: www/wiki-backup/ParallelSwingTimelines.wiki.txt
===================================================================
--- www/wiki-backup/ParallelSwingTimelines.wiki.txt     (revision 0)
+++ www/wiki-backup/ParallelSwingTimelines.wiki.txt     (revision 107)
@@ -0,0 +1,147 @@
+== Multiple timelines in Swing applications ==
+
+Trident supports running multiple independent timelines at the same time. 
This page shows the Swing application behind [http://vimeo.com/3404285 this ;
video], where every cell rollover is implemented as a separate timeline.
+
+We start with a class that implements a specific grid cell:
+
+<pre name="java">
+       public static class SnakePanelRectangle {
+               private Color backgroundColor;
+
+               private boolean isRollover;
+
+               private Timeline rolloverTimeline;
+
+               public SnakePanelRectangle() {
+                       this.backgroundColor = Color.black;
+                       this.isRollover = false;
+
+                       this.rolloverTimeline = new Timeline(this);
+                       
this.rolloverTimeline.addPropertyToInterpolate("backgroundColor",
+                                       Color.yellow, Color.black);
+                       this.rolloverTimeline.setDuration(2500);
+               }
+
+               public void setRollover(boolean isRollover) {
+                       if (this.isRollover == isRollover)
+                               return;
+                       this.isRollover = isRollover;
+                       if (this.isRollover) {
+                               this.rolloverTimeline.replay();
+                       }
+               }
+
+               public void setBackgroundColor(Color backgroundColor) {
+                       this.backgroundColor = backgroundColor;
+               }
+
+               public Color getBackgroundColor() {
+                       return backgroundColor;
+               }
+       }
+</pre>
+
+A few major points in this class:
+* The default background color of a cell is black (line 9). 
+* The rollover timeline interpolates the background color from yellow to 
black over a period of 2.5 seconds (lines 12-15).
+* The rollover timeline is replayed when <code>setRollover</code> is called 
with <code>true</code>. This restarts the timeline to interpolate the 
foreground color from yellow.
+
+The next class implements a cell grid, tracing the mouse events and 
dispatching the rollover events to the relevant cells:
+
+<pre name="java">
+       private static class SnakePanel extends JPanel {
+
+               private SnakePanelRectangle[][] grid;
+
+               private int ROWS = 10;
+
+               private int COLUMNS = 20;
+
+               private int DIM = 20;
+
+               public SnakePanel() {
+                       this.grid = new SnakePanelRectangle[COLUMNS][ROWS];
+                       for (int i = 0; i < COLUMNS; i++) {
+                               for (int j = 0; j < ROWS; j++) {
+                                       this.grid[i][j] = new 
SnakePanelRectangle();
+                               }
+                       }
+                       this.setPreferredSize(new Dimension(COLUMNS * (DIM + 
1), ROWS
+                                       * (DIM + 1)));
+
+                       Timeline repaint = new SwingRepaintTimeline(this);
+                       repaint.playLoop(RepeatBehavior.LOOP);
+
+                       this.addMouseMotionListener(new MouseMotionAdapter() {
+                               int rowOld = -1;
+                               int colOld = -1;
+
+                               @Override
+                               public void mouseMoved(MouseEvent e) {
+                                       int x = e.getX();
+                                       int y = e.getY();
+
+                                       int column = x / (DIM + 1);
+                                       int row = y / (DIM + 1);
+
+                                       if ((column != colOld) || (row != 
rowOld)) {
+                                               if ((colOld >= 0) && (rowOld 
>= 0))
+                                                       
grid[colOld][rowOld].setRollover(false);
+                                               
grid[column][row].setRollover(true);
+                                       }
+                                       colOld = column;
+                                       rowOld = row;
+                               }
+                       });
+               }
+
+               @Override
+               protected void paintComponent(Graphics g) {
+                       Graphics2D g2d = (Graphics2D) g.create();
+
+                       g2d.setColor
[truncated due to length]



[trident~source:107] Wiki pages backup

kirillg 03/08/2010
  • Mysql
  • Glassfish
  • Jruby
  • Rails
  • Nblogo
Terms of Use; Privacy Policy;
© 2010, Oracle Corporation and/or its affiliates
(revision 20120127.ac94057)
 
 
Close
loading
Please Confirm
Close