<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file except in
compliance with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<document id="effects.transitions">
    <properties>
        <title>Transitions</title>
    </properties>

    <body>
        <p>
            Transitions are used to execute a set of operations over a given period of time,
            producing an animation effect. For example, they are used to slide sheets in and out of
            view, fade menu items, and "slide" from one card to another in a card pane.
        </p>

        <p>
            Because transitions are often very specific to the item on which they operate, Pivot
            does not include any stock transitions. However, it is very easy to create one. The
            following application demonstrates the use of a "collapse transition" that causes a
            button to shrink and fade as it is removed from its parent container:
        </p>

        <application class="org.apache.pivot.wtk.ScriptApplication"
            width="180" height="40">
            <libraries>
                <library>core</library>
                <library>wtk</library>
                <library>wtk-terra</library>
                <library>tutorials</library>
            </libraries>
            <startup-properties>
                <src>/org/apache/pivot/tutorials/effects/transitions.bxml</src>
            </startup-properties>
        </application>

        <p>The BXML source for the example is shown below. It simply creates a box pane containing several push buttons:</p>

        <source type="xml" location="org/apache/pivot/tutorials/effects/transitions.bxml">
            <![CDATA[
            <effects:Transitions title="Transitions" maximized="true"
                xmlns:bxml="http://pivot.apache.org/bxml"
                xmlns:effects="org.apache.pivot.tutorials.effects"
                xmlns="org.apache.pivot.wtk">
                <BoxPane styles="{padding:4, spacing:4}">
                    <PushButton bxml:id="button1" buttonData="One"/>
                    <PushButton bxml:id="button2" buttonData="Two"/>
                    <PushButton bxml:id="button3" buttonData="Three"/>
                    <PushButton bxml:id="button4" buttonData="Four"/>
                </BoxPane>
            </effects:Transitions>
            ]]>
        </source>

        <p>
            The Java source for the application is below. It attaches a button press listener to
            each button that starts the collapse transition when the button is pressed (or reverses
            it if one is already running):
        </p>

        <source type="java" location="org/apache/pivot/tutorials/effects/Transitions.java">
            <![CDATA[
            package org.apache.pivot.tutorials.effects;

            import java.net.URL;

            import org.apache.pivot.beans.Bindable;
            import org.apache.pivot.collections.Map;
            import org.apache.pivot.util.Resources;
            import org.apache.pivot.wtk.Button;
            import org.apache.pivot.wtk.ButtonPressListener;
            import org.apache.pivot.wtk.Component;
            import org.apache.pivot.wtk.PushButton;
            import org.apache.pivot.wtk.Window;
            import org.apache.pivot.wtk.effects.Transition;
            import org.apache.pivot.wtk.effects.TransitionListener;

            public class Transitions extends Window implements Bindable {
                private PushButton button1 = null;
                private PushButton button2 = null;
                private PushButton button3 = null;
                private PushButton button4 = null;

                private CollapseTransition collapseTransition = null;

                public static int TRANSITION_DURATION = 250;
                public static int TRANSITION_RATE = 30;

                @Override
                public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
                    button1 = (PushButton)namespace.get("button1");
                    button2 = (PushButton)namespace.get("button2");
                    button3 = (PushButton)namespace.get("button3");
                    button4 = (PushButton)namespace.get("button4");

                    ButtonPressListener buttonPressListener = new ButtonPressListener() {
                        @Override
                        public void buttonPressed(final Button button) {
                            if (collapseTransition == null) {
                                collapseTransition = new CollapseTransition(button, TRANSITION_DURATION, TRANSITION_RATE);

                                TransitionListener transitionListener = new TransitionListener() {
                                    @Override
                                    public void transitionCompleted(Transition transition) {
                                        CollapseTransition collapseTransition = (CollapseTransition)transition;

                                        if (!transition.isReversed()) {
                                            Component component = collapseTransition.getComponent();
                                            component.getParent().remove(component);
                                        }

                                        Transitions.this.collapseTransition = null;
                                    }
                                };

                                collapseTransition.start(transitionListener);
                            } else {
                                collapseTransition.reverse();

                                if (collapseTransition.getComponent() != button) {
                                    collapseTransition.end();
                                }
                            }
                        }
                    };

                    button1.getButtonPressListeners().add(buttonPressListener);
                    button2.getButtonPressListeners().add(buttonPressListener);
                    button3.getButtonPressListeners().add(buttonPressListener);
                    button4.getButtonPressListeners().add(buttonPressListener);
                }
            }
            ]]>
        </source>

        <p>
            Finally, the source for the transition itself is shown below:
        </p>

        <source type="java" location="org/apache/pivot/tutorials/effects/CollapseTransition.java">
            <![CDATA[
            package org.apache.pivot.tutorials.effects;

            import org.apache.pivot.wtk.Component;
            import org.apache.pivot.wtk.effects.FadeDecorator;
            import org.apache.pivot.wtk.effects.Transition;
            import org.apache.pivot.wtk.effects.TransitionListener;
            import org.apache.pivot.wtk.effects.easing.Easing;
            import org.apache.pivot.wtk.effects.easing.Quadratic;

            public class CollapseTransition extends Transition {
                private Component component;
                private int initialWidth;
                private Easing easing = new Quadratic();
                private FadeDecorator fadeDecorator = new FadeDecorator();

                public CollapseTransition(Component component, int duration, int rate) {
                    super(duration, rate, false);

                    this.component = component;
                    initialWidth = component.getWidth();
                }

                public Component getComponent() {
                    return component;
                }

                @Override
                public void start(TransitionListener transitionListener) {
                    component.getDecorators().add(fadeDecorator);

                    super.start(transitionListener);
                }

                @Override
                public void stop() {
                    component.getDecorators().remove(fadeDecorator);

                    super.stop();
                }

                @Override
                protected void update() {
                    float percentComplete = getPercentComplete();

                    if (percentComplete < 1.0f) {
                        int duration = getDuration();
                        int width = (int)(initialWidth * (1.0f - percentComplete));

                        width = (int)easing.easeInOut(getElapsedTime(), initialWidth, width - initialWidth, duration);

                        component.setPreferredWidth(width);

                        fadeDecorator.setOpacity(1.0f - percentComplete);
                        component.repaint();
                    }
                }
            }
            ]]>
        </source>

        <p>
            In the transition's <tt>start()</tt> method, it attaches a fade decorator to the
            component, which is later used by the <tt>update()</tt> method to produce the fade
            part of the effect. The decorator is removed in <tt>stop()</tt>.
        </p>

        <p>
            The <tt>update()</tt> method is called periodically to update the state of the
            transition. In this example, it will be called 30 times per second for a duration of
            250 milliseconds. Each time <tt>update()</tt> is called, the transition adjusts the
            opacity of the fade decorator to the inverse of the completion percentage and sets the
            component's preferred width to the given percentage of the component's initial width
            when the transition started. When the transition is complete, the opacity value will be
            zero, as will its preferred width. It is then removed from the parent box pane by the
            transition listener that had been passed to the <tt>start()</tt> method.
        </p>
    </body>
</document>
