Timeline scenarios in the Trident animation library allow combining multiple timeline scenario actors in a parallel, sequential or custom order. Out of the box, Trident supports timelines, extensions of Runnable class and extensions of SwingWorker class, but the applications can easily create a custom implementation of the TimelineScenarioActor interface.
SwingWorker is an indispensable tool in the arsenal of a Swing programmer, and allows separating the long-running tasks that run on background threads from updating the UI components that must happen on the Event Dispatch Thread. While SWT does not have a direct counterpart, Eclipse core libraries have a very similar concept in Eclipse Jobs. And while the full functionality of Eclipse Jobs allows arbitrary dependencies and fine grained scheduling of interrelated jobs, you can wrap an Eclipse Job as a custom Trident timeline scenario actor and use the TimelineScenario APIs to seamlessly integrate an Eclipse Job in your Trident-powered SWT application.
Here is the complete source code to do this:
import org.eclipse.core.runtime.jobs.*;
import org.pushingpixels.trident.TimelineScenario.TimelineScenarioActor;
public abstract class EclipseJobTimelineScenarioActor extends Job implements
TimelineScenarioActor {
volatile transient boolean isDone = false;
public EclipseJobTimelineScenarioActor(String name) {
super(name);
this.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
isDone = true;
}
});
}
@Override
public boolean isDone() {
return isDone && (this.getState() == Job.NONE);
}
@Override
public void play() {
this.schedule();
}
@Override
public void resetDoneFlag() {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsReplay() {
return false;
}
}
The play() method calls Job.schedule(), scheduling it for immediate execution. The isDone() method is called internally by the Trident engine on every pulse. The implementation registers a JobChangeListener to track the state of the job, and returns the relevant boolean value. Just as with SwingWorkers, the supportsReplay() returns false, and resetDoneFlag() throws an exception.
The timeline scenario below has the following steps which run in a sequential fashion:
- Load an image from the specified URL.
- Scale it to fit the specified area
- Fade it in on the screen
The first step is done using our EclipseJobTimelineScenarioActor (which would be done with a TimelineSwingWorker in a Swing application):
private TimelineScenario getLoadImageScenario(final Item albumItem) {
TimelineScenario loadScenario = new TimelineScenario.Sequence();
// load the image
EclipseJobTimelineScenarioActor imageLoadWorker = new EclipseJobTimelineScenarioActor(
"Load image") {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
URL url = new URL(albumItem.getMediumImage().getURL());
image = new Image(Display.getDefault(), url.openStream());
return Status.OK_STATUS;
} catch (Throwable t) {
t.printStackTrace();
return Status.CANCEL_STATUS;
}
}
};
loadScenario.addScenarioActor(imageLoadWorker);
// scale if necessary
TimelineRunnable scaler = new TimelineRunnable() {
@Override
public void run() {
if (image != null) {
float vFactor = (float) OVERVIEW_IMAGE_DIM
/ (float) image.getImageData().height;
float hFactor = (float) OVERVIEW_IMAGE_DIM
/ (float) image.getImageData().width;
float factor = Math.min(1.0f, Math.min(vFactor, hFactor));
if (factor < 1.0f) {
// scaled to fit available area
image = GraniteUtils.getScaledInstance(image,
(int) (factor * image.getImageData().width),
(int) (factor * image.getImageData().height));
}
imageLoadedDone = true;
}
}
};
loadScenario.addScenarioActor(scaler);
// and fade it in
Timeline imageFadeInTimeline = new Timeline(AlbumOverviewComponent.this);
imageFadeInTimeline.addPropertyToInterpolate("imageAlpha", 0.0f, 1.0f);
imageFadeInTimeline.addCallback(new SWTRepaintCallback(
AlbumOverviewComponent.this));
imageFadeInTimeline.setDuration(500);
loadScenario.addScenarioActor(imageFadeInTimeline);
return loadScenario;
}
You will need the following Eclipse libraries in your classpath:
- org.eclipse.core.jobs
- org.eclipse.equinox.common
- org.eclipse.osgi
As you can see, it is quite easy to extend the existing functionality of Trident scenarios by wrapping external modules as custom timeline scenario actors.
Trident animation library allows adding a short fade-out sequence to a Swing / SWT window that is being closed. This can provide an unobtrusive visual feedback to the user, confirming his action and smoothly guiding his eye to the new application state.
Here is a utility method to add a fade-out sequence to disposing a Swing window – this code requires the latest builds of JDK7 that added the Window.setAlpha(float) method:
public static void fadeOutAndDispose(final Window window,
int fadeOutDuration) {
Timeline dispose = new Timeline(window);
dispose.addPropertyToInterpolate("opacity", 1.0f, 0.0f);
dispose.addCallback(new UIThreadTimelineCallbackAdapter() {
@Override
public void onTimelineStateChanged(TimelineState oldState,
TimelineState newState, float durationFraction,
float timelinePosition) {
if (newState == TimelineState.DONE) {
window.dispose();
}
}
});
dispose.setDuration(fadeOutDuration);
dispose.play();
}
and the matching method for disposing an SWT shell with a fade-out sequence, which requires SWT 3.4 to run:
public static void fadeOutAndDispose(final Shell shell, int fadeOutDuration) {
Timeline dispose = new Timeline(shell);
dispose.addPropertyToInterpolate("alpha", 255, 0);
dispose.addCallback(new UIThreadTimelineCallbackAdapter() {
@Override
public void onTimelineStateChanged(TimelineState oldState,
TimelineState newState, float durationFraction,
float timelinePosition) {
if (newState == TimelineState.DONE) {
shell.dispose();
}
}
});
dispose.setDuration(fadeOutDuration);
dispose.play();
}
Instead of calling Shell.dispose() or Window.dispose(), call the fadeOutAndDispose() method, passing the duration of the fade-out sequence in milliseconds. A previous entry has discussed another option – overriding the Window.dispose() method in your custom Swing class. While this works in Swing, SWT does not allow extending the Shell class outside the org.eclipse.swt.widgets package.
I am thrilled today to announce the availability of the final release for version 1.0 of Trident animation library for Java applications (code-named Acumen). Trident aims to simplify the development of rich animation effects in Java based UI applications, addressing both simple and complex scenarios – and you can read the available documentation in the project Wiki.
The current published API set follows the “simplicity before generality” approach. Trident is a continuation of the internal animation engine that has been part of the Substance look-and-feel for the last two years. Extracting it to a standalone library was accompanied by a significant overhaul of the API facets to:
- Provide a shallow learning curve
- Address real world use cases
It is very easy to start with Trident. To add animations to your application, simply create a timeline, configure it to change a value of some property and play it. From here, you can go progressively deeper towards the more powerful – and the more complex – Trident APIs:
At each level you get more control over the animations – as you get more comfortable with what Trident can do to address the animation requirements of your application.
During the development of this version i have created a number of simple and more advanced examples using Trident. These examples have driven the current shape of Trident APIs. The simple examples include animating the foreground color of a button, showing an indeterminate progress indication, emulating fireworks and Matrix rain. In addition, Amber and Onyx are more complicated applications that integrate animation scenarios into UIs that fetch and display information from the web-based backend services – such as Digg, Twitter and Amazon. These examples strive to be the blueprints for using Trident in Java applications.
If i had to choose three features that bring the most functionality to interested applications, those would be:
- Timeline scenarios that allow creating progressively complex dependency graphs of timelines, runnables, swing workers and custom application actors
- Support for threading rules of UI toolkits that frees the application code from creating convoluted nested inner classes and prevents it from deadlocking and freezing the UI
- The extensibility layer that allows application to extend the existing core functionality to additional property classes and UI toolkits
Going forward, i intend to evolve Trident, and i already have a couple of post-1.0 features in the pipeline. The next major release of Substance will be rewritten to use Trident – further testing the published APIs for usage in real-world scenarios. In addition, the next major release of Flamingo ribbon will add Trident-based animations – where applicable.
Finally, no project is complete without the users trying the different features, pushing the existing APIs, reporting bugs and asking to support additional requirements. Subscribe to the mailing lists and let me know what is missing, and how the existing APIs can be improved. If you find a bug, report it in the issue tracker. If you want to take a look at the code, check out the SVN repository and subscribe to the “commits” mailing list.
After talking with Alex Imrie about usability, it’s time to ask her a few questions about one of the tools her company is working on – GUIDancer. Following a similar interview with Alex Ruiz, creator of FEST, this interview delves deeper into the subject of testing desktop and web UIs.
Tell us a little bit about yourself
I’m Alex Imrie, and I work at BREDEX GmbH in Braunschweig, Germany. I have various roles at the company including Marketing, customer demonstrations, training and support as well as documentation and conceptual design for some of our software. I also do some testing with our automated test tool, GUIdancer.
What is GUIdancer?
GUIdancer is a testing tool for automating functional tests through the GUI. In essence, tests that are usually performed manually can be automated with GUIdancer. We currently support applications with Java (Swing, SWT/RCP) and HTML user interfaces. GUIdancer is a black-box tool and differs from other similar tools in that it uses the keyword-driven approach to testing. Keyword-driven testing is a method which is very close to the principles of software development without actually requiring that code be written. Because GUI tests consist of the same repeated actions, there is a focus on reusability. Tests can be created from a running application or parallel to software development, independently of an application under test, from a library of actions by drag & drop. Each module is named according to the actions it executes, and can be reused (referenced) throughout the test. This reusability means that tests grow quickly and are easy to maintain because central changes update all the instances where a module was reused.
Why did you decide to focus mainly on testing the Java UIs?
Since Bredex was founded in 1987, most of our projects have involved user interfaces, so there has been a focus on GUI testing since the very early days. From 1995, we specialised in Java, so when we decided to write our own test tool, the choice was obvious which technology we were going to start with. The irony was, the first toolkit we supported was Swing, and GUIdancer itself is written in RCP. Since the release of version 2.0 we’ve been able to test GUIdancer with GUIdancer, and we also added the support for HTML testing. The architecture of GUIdancer means that any interface can be tested; it’s just a question of seeing which direction we plan to go in next.
UI testing doesn’t seem to receive its share of the “limelight”, even in the currently popular test driven development paradigm. Is it too difficult or is it just seen as less important?
I think that there is certainly the perception that it is too difficult. A lot of people have been burned by failed attempts or have started with the wrong expectations of functional test automation. UI testing, even in the test driven development paradigm, is by no means impossible. It’s just important to keep realistic aims in mind – automation of repetitive tasks first, for example, or simply having stable regression tests for core features that run as soon as a new piece of the software becomes available. Sure, there are difficulties with functional testing, but these can be avoided by taking the time to identify clear goals and design and plan the tests. With GUIdancer, we see that the tester to developer ratio on a project is as low as 1:10, so the support in the tool for structure and planning pays off well.
Having said that, I think that the importance of UI testing does also tends to be forgotten. It is certainly possible to test a great deal with JUnit, for example, and such tests are incredibly important. However, there is also the need to test the application from the user’s perspective. Can the simplest use case be easily completed via the GUI? Can the application be brought into an irregular state by user actions? On another important level – does the application even do what the customer ordered? These are areas where UI testing really shines, and where JUnit alone doesn’t suffice.
Should testing infrastructure be part of a UI toolkit or is this better left to interested third parties?
As an interested third party, I fear my answer may be somewhat biased! There is a certain charm to a centralised test framework, but I think that third parties are better positioned to know how the toolkit is used in practice and what deviations from the standard are common. There is also the argument that different teams and organisations use different approaches to testing and even different skillsets in the testing team. In all aspects, I believe, there is simply too much variation for a central infrastructure.
Have you looked into the scenegraph approach to building UIs in JavaFX? Does it present significant challenges to existing Java UI testing toolkits?
We haven’t looked into it in detail, but I think the scenegraph approach could pose certain challenges, yes. The animation aspects would mean that timing and movement have to be considered in the tests – there would have to be some pretty good synchronization to ensure robustness, I think.
Do you see desktop applications as a dying breed, with all the significant advantages of browser hosted solutions?
Web applications are certainly very popular and I doubt that this popularity is going to end soon. I also doubt, though, that they will completely replace desktop applications. There is still a strong demand for local applications – which do not need the various capabilities (and complications) that come with browser solutions. One reason I quite like desktop applications is because they are generally more ergonomic and usable. I think web applications have a lot of catching up do to in this respect. Local applications have better dialog support and don’t need to be manually refreshed quite as often. I also see many web applications that pack way too many things on one page so that scrolling (in all directions) is unavoidable.
Would you like to share the future plans for GUIdancer?
We are working towards the release of version 3.1 at the moment, which will be released in the second week of July. This release will see the introduction of automated testing for GEF applications, which I’m very excited about! GUIdancer 3.1 will also be compatible with the Eclipse Galileo release. There will be a few more goodies too, like more supported actions on tables and better support for native dialogs. 3.2 and 4.0 are already being planned, with more toolkits and browsers on the list, as well as a test execution manager and more possibilities to manage test data.