All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.managers.animation.transition.QueueTransition Maven / Gradle / Ivy

There is a newer version: 1.2.14
Show newest version
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.managers.animation.transition;

import com.alee.api.annotations.NotNull;
import com.alee.managers.animation.AnimationException;
import com.alee.managers.animation.framerate.FrameRate;
import com.alee.utils.CollectionUtils;

import java.util.*;

/**
 * {@link Transition} implementation that allows convenient grouping of consecutive transitions.
 * Grouped transitions have a strict order and could be looped indefinitely until this transition is aborted.
 * To add delays {@link IdleTransition}
 *
 * @param  transition value type
 * @author Mikle Garin
 * @see How to use AnimationManager
 * @see com.alee.managers.animation.AnimationManager
 */
public class QueueTransition extends AbstractTransition
{
    /**
     * Whether or not transitions should be repeated in loop until aborted.
     */
    protected boolean looped;

    /**
     * Queue transitions.
     */
    protected final List> elements;

    /**
     * Separate lock for transition elements list.
     */
    protected final Object elementsLock;

    /**
     * Transitions queue.
     */
    protected Queue> queue;

    /**
     * Constructs new queue transition.
     *
     * @param transitions transitions to queue
     */
    public QueueTransition ( final Transition... transitions )
    {
        this ( false, transitions );
    }

    /**
     * Constructs new queue transition.
     *
     * @param transitions transitions to queue
     */
    public QueueTransition ( final List> transitions )
    {
        this ( false, transitions );
    }

    /**
     * Constructs new queue transition.
     *
     * @param looped      whether or not transitions should be repeated in loop until aborted
     * @param transitions transitions to queue
     */
    public QueueTransition ( final boolean looped, final Transition... transitions )
    {
        this ( looped, CollectionUtils.asList ( transitions ) );
    }

    /**
     * Constructs new queue transition.
     *
     * @param looped      whether or not transitions should be repeated in loop until aborted
     * @param transitions transitions to queue
     */
    public QueueTransition ( final boolean looped, final List> transitions )
    {
        super ();
        this.looped = looped;
        this.elements = new ArrayList> ( transitions.size () );
        this.elementsLock = new Object ();
        add ( transitions );
    }

    @Override
    public void setFrameRate ( final FrameRate frameRate )
    {
        // Ensure that proper frame rate is set
        super.setFrameRate ( new QueueFrameRate () );
    }

    /**
     * Returns whether or not transitions should be repeated in loop until aborted.
     *
     * @return {@code true} if transitions should be repeated in loop until aborted, {@code false} otherwise
     */
    public boolean isLooped ()
    {
        return looped;
    }

    /**
     * Sets whether or not transitions should be repeated in loop until aborted.
     *
     * @param looped whether or not transitions should be repeated in loop until aborted
     */
    public void setLooped ( final boolean looped )
    {
        this.looped = looped;
    }

    /**
     * Adds all specified transitions for execution.
     * Note that this will have no effect on the current transition if it is already playing.
     *
     * @param transitions transitions to add
     */
    public void add ( final Transition... transitions )
    {
        synchronized ( elementsLock )
        {
            Collections.addAll ( elements, transitions );
        }
    }

    /**
     * Adds all specified transitions for execution.
     * Note that this will have no effect on the current transition if it is already playing.
     *
     * @param transitions transitions to add
     */
    public void add ( final List> transitions )
    {
        synchronized ( elementsLock )
        {
            elements.addAll ( transitions );
        }
    }

    /**
     * Removes all previously added transitions from execution.
     * Note that this will have no effect on the current transition if it is already playing.
     */
    public void clear ()
    {
        synchronized ( elementsLock )
        {
            elements.clear ();
        }
    }

    @Override
    public FrameRate getFrameRate ()
    {
        return frameRate;
    }

    @Override
    public long getStartTime ()
    {
        synchronized ( QueueTransition.this )
        {
            switch ( getState () )
            {
                case playing:
                    return queue.element ().getStartTime ();

                case finished:
                    return getLastTransition ().getStartTime ();

                case aborted:
                    // todo Should return last transition used before got aborted

                case ready:
                default:
                    return getFirstTransition ().getStartTime ();
            }
        }
    }

    @Override
    public V getStart ()
    {
        return getFirstTransition ().getStart ();
    }

    @Override
    public V getGoal ()
    {
        return getLastTransition ().getGoal ();
    }

    @Override
    public long start ( final long currentFrame )
    {
        synchronized ( QueueTransition.this )
        {
            // Resetting transition first
            reset ();

            // Starting first queue step
            final long untilNextFrame = queue.element ().start ( currentFrame );

            // Updating state
            setState ( TransitionState.playing );

            // Time until next frame in nanoseconds
            return untilNextFrame;
        }
    }

    @Override
    public long proceed ( final long previousFrame, final long currentFrame )
    {
        synchronized ( QueueTransition.this )
        {
            if ( getState () == TransitionState.playing )
            {
                // Retrieving current queue element
                final Transition current = queue.element ();

                // Performing current transition step
                final long untilNextFrame = current.proceed ( previousFrame, currentFrame );

                // Informing about adjusted value
                fireAdjusted ( getValue () );

                // Transition has finished or was aborted
                if ( untilNextFrame <= 0 )
                {
                    // Ensure transition has finished or was aborted
                    final long adjustedFrame = currentFrame + untilNextFrame;
                    if ( current.getState () == TransitionState.playing )
                    {
                        // Process additional transition frames
                        return proceed ( adjustedFrame, currentFrame );
                    }
                    else
                    {
                        // Removing finished transition step
                        final Transition done = queue.remove ();

                        // Adding step to end if looped
                        if ( isLooped () )
                        {
                            queue.add ( done );
                        }

                        // Checking next step availability
                        if ( !queue.isEmpty () )
                        {
                            // Starting next transition step
                            final long untilStartedNextFrame = queue.element ().start ( adjustedFrame );

                            // Updating value according to new transition
                            fireAdjusted ( getValue () );

                            // Check whether or not we need to process additional transition frames
                            if ( adjustedFrame + untilStartedNextFrame < currentFrame )
                            {
                                // Process additional transition frames
                                return proceed ( adjustedFrame, currentFrame );
                            }
                            else
                            {
                                // Time until next frame in nanoseconds
                                return untilStartedNextFrame;
                            }
                        }
                        else
                        {
                            // Emptying queue
                            queue = null;

                            // Updating state
                            setState ( TransitionState.finished );

                            // Transition queue has finished
                            return untilNextFrame;
                        }
                    }
                }
                else
                {
                    // Time until next frame in nanoseconds
                    return untilNextFrame;
                }
            }
            else
            {
                // Transition is not playing
                return 0;
            }
        }
    }

    @Override
    public void abort ()
    {
        synchronized ( QueueTransition.this )
        {
            if ( getState () == TransitionState.playing )
            {
                // Aborting current transition
                queue.element ().abort ();

                // Emptying queue
                queue = null;

                // Updating state
                setState ( TransitionState.aborted );
            }
        }
    }

    @Override
    public void reset ()
    {
        synchronized ( QueueTransition.this )
        {
            if ( getState () != TransitionState.ready )
            {
                // Ensures that transition is properly configured
                checkConfiguration ();

                // Copying queue elements
                // This is made to avoid modifying initial list and allow resetting it on restart
                queue = new ArrayDeque> ( elements );

                // Updating state
                setState ( TransitionState.ready );
            }
        }
    }

    @Override
    public V getValue ()
    {
        synchronized ( QueueTransition.this )
        {
            switch ( getState () )
            {
                case playing:
                    return queue.element ().getValue ();

                case finished:
                    return getGoal ();

                case aborted:
                    return getLatest ();

                case ready:
                default:
                    return getStart ();
            }
        }
    }

    @Override
    public int compareTo ( @NotNull final Object object )
    {
        return this == object ? 0 : -1;
    }

    /**
     * Specific {@link FrameRate} implementation for {@link QueueTransition}.
     */
    protected class QueueFrameRate implements FrameRate
    {
        @Override
        public double value ()
        {
            synchronized ( QueueTransition.this )
            {
                synchronized ( elementsLock )
                {
                    switch ( getState () )
                    {
                        case playing:
                            return queue.element ().getFrameRate ().value ();

                        case finished:
                        case aborted:
                            return elements.get ( elements.size () - 1 ).getFrameRate ().value ();

                        case ready:
                        default:
                            return elements.get ( 0 ).getFrameRate ().value ();
                    }
                }
            }
        }

        @Override
        public boolean isFixed ()
        {
            synchronized ( elementsLock )
            {
                double frameRate = -1;
                for ( final Transition element : elements )
                {
                    if ( !element.getFrameRate ().isFixed () )
                    {
                        return false;
                    }
                    else if ( frameRate == -1 )
                    {
                        frameRate = element.getFrameRate ().value ();
                    }
                    else if ( frameRate != element.getFrameRate ().value () )
                    {
                        return false;
                    }
                }
                return true;
            }
        }
    }

    /**
     * Returns first available transition.
     *
     * @return first available transition
     */
    protected Transition getFirstTransition ()
    {
        synchronized ( elementsLock )
        {
            // Ensures that transition is properly configured
            checkConfiguration ();

            // Return first available transition
            return elements.get ( 0 );
        }
    }

    /**
     * Returns last available transition.
     *
     * @return last available transition
     */
    protected Transition getLastTransition ()
    {
        synchronized ( elementsLock )
        {
            // Ensures that transition is properly configured
            checkConfiguration ();

            // Return last available transition
            return elements.get ( elements.size () - 1 );
        }
    }

    /**
     * Ensures that transition is properly configured.
     */
    protected void checkConfiguration ()
    {
        synchronized ( elementsLock )
        {
            if ( CollectionUtils.isEmpty ( elements ) )
            {
                throw new AnimationException ( "QueueTransition must contain at least one Transition" );
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy