May 21, 2009
Swing event triggers for Trident animations
Trigger happy ?
Starting and stopping animations is usually related to events occuring somewhere in your ui. Writing those event listeners can be verbose and tedious at times. Chet Haase (one of the great, we miss you buddy) solved this problem in his TimingFramework animation library by providing a small core of extensible classes dealing with events: triggers (example here).
They allow you to say: start this timeline when the user interacts with this button, or start this timeline when the mouse enters that component and reverses it when it exits, and so on.
While testing Kirill’s Trident library, i thought this feature could be nice to have, so i ported Chet’s trigger code over to support it.
Meet the cast
Triggers are useful for one-shot events, like action events, but some events have an “opposite” event (pressing and releasing the mouse, entering and leaving a component, etc). The good thing is you can create an “autoreverse” trigger that will play the timeline backwards when this opposite event occurs: the values will smoothly be animated back to their original value. This is great for hover animations for instance.
Almost all of the original triggers have been ported except for the ones that didn’t make sense in this new context (TF uses triggers on animations to group them, in sequence or in parallel – eg start this animation when this one stops – and Trident supports that use case with timeline scenarios, so i didn’t feel they were needed here, feel free to comment on those)
Specifically, the supported event triggers are:
- Mouse triggers: enter, exit, press, release, click
- Action triggers: supporting actionPerformed
- Focus triggers: gain, loss
The How
In the zip file linked at the bottom, you’ll find the simplest Trident demo modified to show how you’d use triggers. With that, the javadoc and the simple API, everything should be self explanatory.
Here is the one liner you just need to write to have a hover animation – while the mouse is over a button.
MouseTrigger.addTrigger (button, timeline, MouseTriggerEvent.ENTER, true);
And here’s the one starting a timeline when the button is pushed.
ActionTrigger.addTrigger (button, timeline);
Pretty simple. Thanks Chet ! (:
In the future, it might be possible to have triggers apply automatically to the object you’re animating. I’m not sure you can get that object back from the timeline right now, but it could be interesting to have for this very reason, and also to have callbacks that know about the main object without needing to give it to them (saves some code for repaints for instance). We’ll see with Kirill about that.
Extensibility, and porting existing triggers
Triggers are definitely an interesting concept, and the better thing is they can be extended easily. Maybe one could need triggers for other events. Feel free to ask, or do it yourselves (and maybe we’ll backport them to TF too, then).
For instance triggering animations on mouse wheel or drag and drop events, property changes, or maybe virtual events resulting from other changes. Those could be really useful for saying: start this animation when the mouse dragged this widget more than 10 pixels to the left, or my favorite (kinda hard to do at the moment, but i’m working on it) fade in and grow the radius of this motion blur when this widget is moving faster than x pixels/second and reverse that when it slows down (Those are the kind of features you usually only see in SFX tools like After Effects and the like, and i’d love to have that kind of power in Java too).
If you have an existing TF trigger, or if you find one you like on the intertubes and would want to use it with Trident, about the only thing you’ll need to do is replace the references to TF’s Animator to Trident’s Timeline, and that should be it since i’ve kept the API exactly the same (except renaming the focus constants, a subjective change, i thought FocusTriggerEvent.GAINED was more natural than FocusTriggerEvent.IN, could be better as FocusTriggerEvent.GAIN since the mouse’s are MouseTriggerEvent.ENTER and not ENTERED for instance, definitely something we can talk about). Let me know if you run into problems.
Creating one should be really easy too, just looking at the code for the core ones should convince you of that.
Okay, i’m sold, sir, where do i sign ?
Binaries here, source here (eclipse project, also includes the latest available trident jar as of right now, rev 42), sign here, credit card there, thanks mam and have a nice day.
As you probably know if you’ve read my previous posts, i’m more involved with Scenario at the moment (the über cool java thingy hidden under the arguably less cool javafx script thingy in the javafx runtime) and its animation support (based on evolving TF). However, I strongly encourage you to look at Trident, and its demos (+ Amber and Onyx) if you plan on adding animations to your java/swing apps (which you should, for basic usability reasons – hello, transitions! – at the very least).
Both Trident and TF are great animation libraries, the advantages Trident has, even though it’s probably less mature, is that the project is alive (TF’s state is not really clear, dormant, dead, spending spring break in Cancun, i don’t know), has some features TF is missing, and that it’s made by Kirill. TF’s are that it’s older and for that, probably used more, it has features Trident is missing, is more documented (the most recent doc is in a book, printed on real paper ! but you have to pay for it) and is made by Chet. Scenario’s animation support is good but has problems, mainly the license and the fact that it’s dead for the open source version, and the license and Oracle in the picture for the proprietary version (+ it’s proprietary i just said it).
So, triggers huh ? Hope you like it, see you soon (with something totally unrelated), or sooner than that on twitter.
xoxoxo
Rémy
These bind somewhat resemble one of my approaches for regular swing events; binding listeners provided by a component with methods provided by a POJO.
For example:
SwingEventDispatcher.wrap(new JButton(“A Button”), “myButton”, this)
This binds any method in “this” to the listeners provided by JButton, e.g.
public void myButton_click(…)
public void myButton_mouseEntered(…)
That’s an interesting approach.
I’ve also experimented with the way people do event listening in qt/gtk/etc, in brief with slots and signals. In java it looks a lot like in c#, you have an object that reifies the event, and you use it to add listeners, fire it, etc. Which i’ve done using the composite trick i previously talked about.
At this point, i’m not sure if there’s any approach that is definitely the best, as it seems to be more a matter of API taste than features per se, since they all provide basically the same benefits.
ya the trident library and timing framework are really great for sure!
i also use both of them in my Java Desktop application.
and it is really helping me alot! pheewww…! :D