blob: fa64a8152b1d43517cf28830f5db6bc86bba9d44 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.internal;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
/**
* This job creates an animated rectangle that moves from a source rectangle to
* a target in a fixed amount of time. To begin the animation, instantiate this
* object then call schedule().
*
* @since 3.0
*/
public class RectangleAnimation extends Job {
private Rectangle start;
private int elapsed;
private int duration;
private long startTime = 0;
private Rectangle end;
private Rectangle last;
private boolean done = false;
/**
* Canvas used to draw the animation, or null if the animation should be skipped.
*/
private Canvas canvas;
private static Rectangle interpolate(Rectangle start, Rectangle end, double amount) {
double initialWeight = 1.0 - amount;
Rectangle result = new Rectangle(
(int) (start.x * initialWeight + end.x * amount),
(int) (start.y * initialWeight + end.y * amount),
(int) (start.width * initialWeight + end.width * amount),
(int) (start.height * initialWeight + end.height * amount));
return result;
}
private Runnable paintJob = new Runnable() { //$NON-NLS-1$
public void run() {
if (canvas == null || canvas.isDisposed()) {
done = true;
return;
}
canvas.redraw();
}
};
private void draw(GC gc) {
if (startTime == 0) {
return;
}
if (canvas == null || canvas.isDisposed()) {
done = true;
return;
}
long currentTime = System.currentTimeMillis();
double amount = (double)(currentTime - startTime) / (double)duration;
if (amount > 1.0) {
amount = 1.0;
done = true;
}
Rectangle toPaint = interpolate(start, end, amount);
gc.setLineWidth(2);
Color color = canvas.getDisplay().getSystemColor(SWT.COLOR_WHITE);
gc.setForeground(color);
gc.setXORMode(true);
if (last != null) {
if (last.equals(toPaint)) {
return;
}
gc.drawRectangle(Geometry.toControl(canvas, last));
}
gc.drawRectangle(Geometry.toControl(canvas, toPaint));
last = toPaint;
}
/**
* Creates an animation that will morph the start rectangle to the end rectangle in the
* given number of milliseconds. The animation will take the given number of milliseconds to
* complete.
*
* Note that this is a Job, so you must invoke schedule() before the animation will begin
*
* @param whereToDraw specifies the composite where the animation will be drawn. Note that
* although the start and end rectangles can accept any value in display coordinates, the
* actual animation will be clipped to the boundaries of this composite. For this reason,
* it is good to select a composite that encloses both the start and end rectangles.
* @param start initial rectangle (display coordinates)
* @param end final rectangle (display coordinates)
* @param duration number of milliseconds over which the animation will run
*/
public RectangleAnimation(Composite whereToDraw, Rectangle start, Rectangle end, int duration) {
super(WorkbenchMessages.getString("RectangleAnimation.Animating_Rectangle")); //$NON-NLS-1$
this.duration = duration;
this.start = start;
this.end = end;
setSystem(true);
// Determine if we're on a platform where animations look ugly.
// If so, we indicate this by setting canvas=null, in which case this job does nothing.
String platform = SWT.getPlatform();
if (!"win32".equals(platform)) { //$NON-NLS-1$
return;
}
this.canvas = new Canvas(whereToDraw, SWT.NO_BACKGROUND);
canvas.setBounds(whereToDraw.getClientArea());
canvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
draw(event.gc);
}
});
canvas.moveAbove(null);
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
protected IStatus run(IProgressMonitor monitor) {
// We use canvas = null to indicate that the animation should be skipped on this platform.
if (canvas == null) {
return Status.OK_STATUS;
}
startTime = System.currentTimeMillis();
while (!done) {
if (!canvas.isDisposed()) {
canvas.getDisplay().syncExec(paintJob);
}
}
if (!canvas.isDisposed()) {
canvas.getDisplay().syncExec(new Runnable() {
public void run() {
canvas.dispose();
}
});
}
return Status.OK_STATUS;
}
}