Mercurial > projects > dwt-addons
diff dwtx/draw2d/Animation.d @ 98:95307ad235d9
Added Draw2d code, still work in progress
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 03 Aug 2008 00:52:14 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/draw2d/Animation.d Sun Aug 03 00:52:14 2008 +0200 @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ + +module dwtx.draw2d.Animation; + +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.draw2d.Animator; +import dwtx.draw2d.IFigure; +import dwtx.draw2d.UpdateManager; + + +/** + * A utility for coordinating figure animations. During animation, multiple + * <i>animators</i> are employed to capture the <em>initial</em> and <em>final</em> states + * for one or more figures. The animators then playback the animation by interpolating the + * intermediate states for each figure using the initial and final "keyframes". + * <P> + * An animator is usually stateless and represents an specific technique. Any state + * information is stored by the Animation utility. Therefore, one instance can be used + * with multiple figures. Animators hook into the validation mechanism for figures and + * connections. These hooks are used to capture the states, and to intercept the typical + * layout process to insert the interpolated state. + * <P> + * To indicate that animation is desired, clients must call {@link #markBegin()} prior to + * invalidating any figures that are to be included in the animation. After this method is + * called, changes are made, and {@link #run()} is invoked. The run method will force a + * validation pass to capture the final states, and then commence the animation. The + * animation is synchronous and the method does not return until the animation has + * completed. + * @see LayoutAnimator + * @since 3.2 + */ +public class Animation { + +static class AnimPair { + + const Animator animator; + const IFigure figure; + + this(Animator animator, IFigure figure) { + this.animator = animator; + this.figure = figure; + } + + public override int opEquals(Object obj) { + AnimPair pair = cast(AnimPair)obj; + return pair.animator is animator && pair.figure is figure; + } + + public override hash_t toHash() { + return (cast(Object)animator).toHash() ^ (cast(Object)figure).toHash(); + } +} +private static const int DEFAULT_DELAY = 250; +private static Set figureAnimators; +private static Map finalStates; + +private static Map initialStates; +private static const int PLAYBACK = 3; +private static float progress; +private static const int RECORD_FINAL = 2; + +private static const int RECORD_INITIAL = 1; +private static long startTime; +private static int state; +private static Set toCapture; + +private static UpdateManager updateManager; + +private static void capture() { + Iterator keys = figureAnimators.iterator(); + while (keys.hasNext()) { + AnimPair pair = cast(AnimPair) keys.next(); + if (toCapture.contains(pair)) + pair.animator.capture(pair.figure); + else + keys.remove(); + } +} + +static void cleanup() { + if (figureAnimators !is null) { + Iterator keys = figureAnimators.iterator(); + while (keys.hasNext()) { + AnimPair pair = cast(AnimPair) keys.next(); + pair.animator.tearDown(pair.figure); + } + } + + state = 0; + step(); + //Allow layout to occur normally + //updateManager.performUpdate(); + + initialStates = null; + finalStates = null; + figureAnimators = null; + updateManager = null; + toCapture = null; + state = 0; +} + +private static void doRun(int duration) { + state = RECORD_FINAL; + findUpdateManager(); + updateManager.performValidation(); + capture(); + state = PLAYBACK; + progress = 0.1f; + startTime = System.currentTimeMillis(); + + notifyPlaybackStarting(); + + while (progress !is 0) { + step(); + updateManager.performUpdate(); + if (progress is 1.0) + progress = 0; + else { + int delta = cast(int)(System.currentTimeMillis() - startTime); + if (delta >= duration) + progress = 1f; + else + progress = 0.1f + 0.9f * delta / duration; + } + } +} + +private static void findUpdateManager() { + AnimPair pair = cast(AnimPair) figureAnimators.iterator().next(); + updateManager = pair.figure.getUpdateManager(); +} + +/** + * Returns the final animation state for the given figure. + * @param animator the animator for the figure + * @param figure the figure being animated + * @return the final state + * @since 3.2 + */ +public static Object getFinalState(Animator animator, IFigure figure) { + return finalStates.get(new AnimPair(animator, figure)); +} + +/** + * Returns the initial animation state for the given animator and figure. If no state was + * recorded, <code>null</code> is returned. + * @param animator the animator for the figure + * @param figure the figure being animated + * @return the initial state + * @since 3.2 + */ +public static Object getInitialState(Animator animator, IFigure figure) { + return initialStates.get(new AnimPair(animator, figure)); +} + +/** + * Returns the animation progress, where 0.0 < progress ≤ 1.0. + * @return the progress of the animation + * @since 3.2 + */ +public static float getProgress() { + return progress; +} + +static void hookAnimator(IFigure figure, Animator animator) { + AnimPair pair = new AnimPair(animator, figure); + if (figureAnimators.add(pair)) + animator.init(figure); +} + +static void hookNeedsCapture(IFigure figure, Animator animator) { + AnimPair pair = new AnimPair(animator, figure); + if (figureAnimators.contains(pair)) + toCapture.add(pair); +} + +static bool hookPlayback(IFigure figure, Animator animator) { + if (toCapture.contains(new AnimPair(animator, figure))) + return animator.playback_package(figure); + return false; +} + +/** + * Returns <code>true</code> if animation is in progress. + * @return <code>true</code> when animating + * @since 3.2 + */ +public static bool isAnimating() { + return state is PLAYBACK; +} + +static bool isFinalRecording() { + return state is RECORD_FINAL; +} + +static bool isInitialRecording() { + return state is RECORD_INITIAL; +} + +/** + * Marks the beginning of the animation process. If the beginning has already been marked, + * this has no effect. + * @return returns <code>true</code> if beginning was not previously marked + * @since 3.2 + */ +public static bool markBegin() { + if (state is 0) { + state = RECORD_INITIAL; + initialStates = new HashMap(); + finalStates = new HashMap(); + figureAnimators = new HashSet(); + toCapture = new HashSet(); + return true; + } + return false; +} + +private static void notifyPlaybackStarting() { + Iterator keys = figureAnimators.iterator(); + while (keys.hasNext()) { + AnimPair pair = cast(AnimPair) keys.next(); + pair.animator.playbackStarting(pair.figure); + } +} + +static void putFinalState(Animator animator, IFigure key, Object state) { + finalStates.put(new AnimPair(animator, key), state); +} + +static void putInitialState(Animator animator, IFigure key, Object state) { + initialStates.put(new AnimPair(animator, key), state); +} + +/** + * Runs animation using the recommended duration: 250 milliseconds. + * @see #run(int) + * @since 3.2 + */ +public static void run() { + run(DEFAULT_DELAY); +} + +/** + * Captures the final states for the animation and then plays the animation. + * @param duration the length of animation in milliseconds + * @since 3.2 + */ +public static void run(int duration) { + if (state is 0) + return; + try { + if (!figureAnimators.isEmpty()) + doRun(duration); + } finally { + cleanup(); + } +} + +private static void step() { + Iterator iter = initialStates.keySet().iterator(); + while (iter.hasNext()) + (cast(AnimPair)iter.next()).figure.revalidate(); +} + +}