AnimatedStyler.java

package swingtree.api;

import swingtree.animation.AnimationStatus;
import swingtree.animation.LifeTime;
import swingtree.style.ComponentStyleDelegate;

import javax.swing.JComponent;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * An {@link AnimatedStyler} is conceptually a union of the {@link swingtree.animation.Animation}
 * and {@link Styler} functions, which is to say that it takes both an {@link AnimationStatus} and a
 * {@link ComponentStyleDelegate} to produce a new {@link ComponentStyleDelegate}
 * with some style properties applied to it (usually based on the {@link AnimationStatus}). <br>
 * Note that both paramters are immutable value oriented objects, so the function is pure and
 * does not modify the original {@link ComponentStyleDelegate} or {@link AnimationStatus} objects. <br>
 * This design makes the underlying style engine of SwingTree very flexible and scalable
 * because it allows for the composition of styles and reuse of style logic across many components
 * (see {@link swingtree.style.StyleSheet} for more advanced usage).
 * <p>
 * This interface is typically used in {@link swingtree.ComponentDelegate#animateStyleFor(LifeTime, AnimatedStyler)}
 * and {@link swingtree.ComponentDelegate#animateStyleFor(double, TimeUnit, AnimatedStyler)}.
 *
 * @param <C> the type of the {@link JComponent} that the {@link ComponentStyleDelegate} is delegating to.
 */
@FunctionalInterface
public interface AnimatedStyler<C extends JComponent>
{
    /**
     * A {@link AnimatedStyler} that does nothing, meaning it simply returns the given {@link ComponentStyleDelegate}
     * without applying any style to it. Conceptually speaking, this returns the null object
     * of the {@link AnimatedStyler} type.
     *
     * @param <C> The type of the {@link JComponent} that the {@link ComponentStyleDelegate} is delegating to.
     * @return A {@link AnimatedStyler} that does nothing.
     */
    static <C extends JComponent> AnimatedStyler<C> none() {
        return (AnimatedStyler<C>) Constants.ANIMATED_STYLER_NONE;
    }

    /**
     * Applies some style to the given {@link ComponentStyleDelegate} and returns a new {@link ComponentStyleDelegate}
     * that has the style applied (if any). <br>
     *  Note that this method deliberately requires the handling of checked exceptions
     *  at its invocation sites because there may be any number of implementations
     *  hiding behind this interface and so it is unwise to assume that
     *  all of them will be able to execute gracefully without throwing exceptions.
     *
     * @param status The {@link AnimationStatus} which is used to configure the style
     *              (usually based on the {@link AnimationStatus#progress()}).
     * @param delegate The {@link ComponentStyleDelegate} to apply the style to.
     * @return A new {@link ComponentStyleDelegate} that has the style applied.
     */
    ComponentStyleDelegate<C> style( AnimationStatus status, ComponentStyleDelegate<C> delegate ) throws Exception;

    /**
     * Returns a new {@link AnimatedStyler} that applies the style of this {@link AnimatedStyler} and then applies the style
     * of the given {@link AnimatedStyler}. <br>
     * This method is conceptually equivalent to the
     * {@link java.util.function.Function#andThen(java.util.function.Function)}.
     *
     * @param other the {@link AnimatedStyler} to apply after this {@link AnimatedStyler}.
     * @return a new {@link AnimatedStyler} that applies the style of this {@link AnimatedStyler} and then applies the style
     * of the given {@link AnimatedStyler}.
     */
    default AnimatedStyler<C> andThen( AnimatedStyler<C> other ) {
        Objects.requireNonNull(other, "Use AnimatedStyler.none() instead of null.");
        if ( this == none() )
            return other;
        if ( other == none() )
            return this;

        return (state, delegate) -> {
            ComponentStyleDelegate<C> result = delegate;
            try {
                result = style( state, delegate );
            } catch ( Exception e ) {
                Constants.LOG.error("Failed to evaluate composed style", e);
            }
            return other.style( state, result );
        };
    }
}