Package org.adamtacy.client.ui.effects

Source Code of org.adamtacy.client.ui.effects.NEffect

/*
* Copyright 2008-2009 Adam Tacy <adam.tacy AT gmail.com>
*
* Licensed 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.
*/
package org.adamtacy.client.ui.effects;

import java.util.Iterator;
import java.util.Vector;

import org.adamtacy.client.ui.NEffectPanel;
import org.adamtacy.client.ui.effects.events.EffectCompletedEvent;
import org.adamtacy.client.ui.effects.events.EffectCompletedHandler;
import org.adamtacy.client.ui.effects.events.EffectInterruptedEvent;
import org.adamtacy.client.ui.effects.events.EffectInterruptedHandler;
import org.adamtacy.client.ui.effects.events.EffectPausedEvent;
import org.adamtacy.client.ui.effects.events.EffectPausedHandler;
import org.adamtacy.client.ui.effects.events.EffectResumedEvent;
import org.adamtacy.client.ui.effects.events.EffectResumedHandler;
import org.adamtacy.client.ui.effects.events.EffectStartingEvent;
import org.adamtacy.client.ui.effects.events.EffectStartingHandler;
import org.adamtacy.client.ui.effects.events.EffectSteppingEvent;
import org.adamtacy.client.ui.effects.events.EffectSteppingHandler;
import org.adamtacy.client.ui.effects.events.HasAllEffectEventHandlers;
import org.adamtacy.client.ui.effects.impl.browsers.EffectImplementation;
import org.adamtacy.client.ui.effects.transitionsphysics.EaseInOutTransitionPhysics;
import org.adamtacy.client.ui.effects.transitionsphysics.TransitionPhysics;

import com.google.gwt.animation.client.Animation;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Timer;


/**
* A class that provides the basics of an effect.
*
* From v4 a new style of setting properties is introduced.
*
* From v3 of GWT-FX this class is based on the GWT Animation class to keep
* divergence from GWT itself as minimal as possible. To maintain the previous
* API, this class extends Animation with a number of fields and methods.
*
* It is an abstract class that provides a fair amount of functionality, but
* leaves the implementation of the
*
* @author adam
*
*/
public abstract class NEffect extends Animation implements
    HasAllEffectEventHandlers {

  /**
   * Determine if an NEffect has started.
   * @return boolean value indicating if NEffect has started or not.
   */
  public boolean isStarted() {
    return effectStarted;
  }

  // Indicates if start > end in play parameters (different to inverse)
  protected boolean backwards = false;

  protected Vector<NEffect> chainedEffects;

  protected double currentEffectPosition = 0.0;
  protected double currentEffectPhysicalPosition = 0.0;

  protected final double DEFAULT_EFFECT_LENGTH = 2.0;

  // Removed by enhancement 104 in favour of a Vector effectElements
  //protected Element effectElement;
 
  // Enhancement 104
  protected Vector<Element> effectElements = new Vector<Element>();


  protected boolean effectFinished = false;
 
  protected boolean effectStarted = false;

  protected double effectLengthSeconds = DEFAULT_EFFECT_LENGTH;

  /**
   * Need this as we're not extending a widget here and so have no access to that HandlerManager.
   */
  HandlerManager handlerManager = new HandlerManager(DEFAULT_EFFECT_LENGTH);

  protected boolean interruptable = true;

  protected boolean inverted = false;

  /**
   * Details if the effect has been initialised yet or not.
   * Updated by either the init(NEffectPanel thePanel) method, or through the play() method.
   */
  protected boolean isInitialised = false;

  // Store the real effect end point - usually this will be at position 1.0
  // unless overridden
  // by the user (see method interpolate for use of this variable).
  double requestedEffectEnd = 1.0;

  // Store the real effect start point - usually this will be at position 0.0
  // unless overridden
  // by the user (see method interpolate for use of this variable).
  double requestedEffectStart = 0.0;

  Timer startDelay;


  /**
   * The effectPanel the effect maybe attached to
   * From v5 this is nt necessary for all effects.
   */
  protected NEffectPanel thePanel;

  /**
   * Holds the Transition for the effect.
   */
  protected TransitionPhysics transition;

  public HandlerRegistration addEffectCompletedHandler(
      EffectCompletedHandler handler) {
    return addHandler(handler, EffectCompletedEvent.getType());
  }

  public HandlerRegistration addEffectInterruptedHandler(
      EffectInterruptedHandler handler) {
    return addHandler(handler, EffectInterruptedEvent.getType());
  }

  public HandlerRegistration addEffectStartingHandler(
      EffectStartingHandler handler) {
    return addHandler(handler, EffectStartingEvent.getType());
  }

  public HandlerRegistration addEffectSteppingHandler(
      EffectSteppingHandler handler) {
    return addHandler(handler, EffectSteppingEvent.getType());
  }
 
  public HandlerRegistration addEffectResumedHandler(
      EffectResumedHandler handler) {
    return addHandler(handler, EffectResumedEvent.getType());
  }
 
  public HandlerRegistration addEffectPausedHandler(
      EffectPausedHandler handler) {
    return addHandler(handler, EffectPausedEvent.getType());
  }
 
  /**
   * Adds a new Element to be managed by this effect.
   * See enhancement 104.
   * @param el
   */
  public void addEffectElement(Element el){
    effectElements.add(el);
  }
 
  /**
   * Adds this handler to the widget.
   *
   * @param <H> the type of handler to add
   * @param type the event type
   * @param handler the handler
   * @return {@link HandlerRegistration} used to remove the handler
   */
  protected final <H extends EventHandler> HandlerRegistration addHandler(
      final H handler, GwtEvent.Type<H> type) {
    return ensureHandlers().addHandler(type, handler);
  }

  /**
   * Override the Animate cancel to check that effect can be interrupted.
   */
  @Override
  public void cancel() {
      if (interruptable)
        super.cancel();
  }
 
  boolean paused = false;
 
  HandlerRegistration chainedEffectsHandlers;

 
  public void chain(final NEffect e) {
    if (chainedEffects == null){
      chainedEffects = new Vector<NEffect>();
    }
    chainedEffects.add(e);
    // Effects are linked together in the init method.
  }
 
  boolean looping = false;
 
  public void setLooping(boolean val) {
    looping = val;
  }
 
  public boolean isLooped(){
    return looping;
  }

 
  /**
   * Unchain all effects that have previously been chained
   */
  public void unchain(){
    if(chainedEffects != null){
      if(chainedEffectsHandlers!=null)chainedEffectsHandlers.removeHandler();
      chainedEffects = null;
      chainedEffectsHandlers = null;
    }
    isInitialised = false;
  }

  /**
   * Ensures the existence of the handler manager.
   *
   * @return the handler manager
   * */
  HandlerManager ensureHandlers() {
    return handlerManager == null ? handlerManager = new HandlerManager(this)
        : handlerManager;
  }

  /**
   * Fires an event. Usually used when passing an event from one source to
   * another.
   *
   * @param event the event
   */
  public void fireEvent(GwtEvent<?> event) {
    if (handlerManager != null) {
      handlerManager.fireEvent(event);
    }
  }

  /**
   * Return the effect length in seconds.
   *
   * @return Effect length in seconds.
   */
  public double getDuration() {
    return effectLengthSeconds;
  }

  /**
   * Returns first element effect is applied to (after enhancement 104 allowed multiple elements)
   * @return
   */
  public Element getEffectElement(){
    Element toReturn = effectElements.get(0);
    if (thePanel!=null) toReturn = thePanel.getElement();
    return toReturn;
  }

  public NEffectPanel getEffectPanel() {
    return thePanel;
  }

  public HandlerManager getHandlers() {
    return handlerManager;
  }

  /**
   * Return the current progress of the effect - a value somewhere between 0 and
   * 1.
   *
   * @return
   */
  public double getProgress() {
    return currentEffectPhysicalPosition;
  }

  public double getProgressInterpolated() {
    return currentEffectPosition;
  }

  /**
   * Return the current Transition Physics Model used by the Effect.
   *
   * @return
   */
  public TransitionPhysics getTransitionPhysics() {
    return transition;
  }

  public void init(){
    if (transition == null)
      transition = new EaseInOutTransitionPhysics();

    if (chainedEffects != null) {
      // Start chaining the effects together
      // First initialise them all
      if(thePanel!=null)
        for (Iterator<NEffect> it = chainedEffects.iterator(); it.hasNext();) {
          it.next().init(thePanel);
        }
      // Add an effect completed handler for this effect to fire all chained effects
      chainedEffectsHandlers = this.addEffectCompletedHandler(new EffectCompletedHandler() {
        public void onEffectCompleted(EffectCompletedEvent event) {
          for (Iterator<NEffect> it = chainedEffects.iterator(); it.hasNext();) {
            it.next().play();
          }
        }
      });
    }
    setUpEffect();
  }
   
  /**
   * Initialises an effect on an effect panel.
   *
   * @param thePanel The EffectPanel on which effect is being applied.
   */
  public void init(NEffectPanel thePanel) {
    this.thePanel = thePanel;
    init();
    isInitialised = true;
  }
 
  /**
   * Overridden version of the Animate class to allow us to swap in and out the
   * transition method. In GWT 1.5 this is a fixed calculation in the Animate
   * class.
   *
   * We also use this method to fire any change events since it is not possible
   * elsewhere in the Animate framework, and in theory interpolate would be
   * called as many times as the update method.
   */
  @Override
  protected double interpolate(double progress) {
    // Store the physical position (i.e. before interpolation)
    currentEffectPhysicalPosition = interpolateWithoutUpdate(progress);
    // Interpolate value based on transition physics
    double newVal = interpolateFinish(currentEffectPhysicalPosition);
   
    // Right, now we've done the maths, time to fire any change listeners that
    // may be registered
   
    // Issue 112 means that EffectSteppingEvents should now be called by override methods.
    // fireEvent(new EffectSteppingEvent(newVal,""));
   
     // and finally, return the value.
    return newVal;
  }
 
  protected double interpolateWithoutUpdate(double progress) {
    // Check if effect is set up yet
    if (transition == null)
      return 1.0;
    // Check if the effect is backwards (e.g. requested start is higher than
    // requested end)
    if (!backwards) {
      // It is not, so just find the real progress
      progress = progress + requestedEffectStart;
      // Check that we are not now past the requested end of effect
      if (progress > requestedEffectEnd)
        progress = requestedEffectEnd;
    } else {
      // Effect is backwards, so just rework the progress calculation
      progress = requestedEffectStart - progress;
      if (progress < requestedEffectEnd)
        progress = requestedEffectEnd;
    }
    if ((progress == 1.0)) {
      if (inverted)
        return 0.0;
      else
        return 1.0;
    }
    if (progress == 0.0) {
      if (inverted)
        return 1.0;
      else
        return 0.0;
    }
    return progress;
  }
 
  private double interpolateFinish(double progress){
    // Now get the position in animation based on the particular Transition to
    // be applied to this effect.
    double val = transition.getAnimationPosition(progress);
    // Finally, check if the effect is inverted, if so, deal with that.
    if (inverted)
      val = 1 - val;
    return val;
  }

  /**
   *
   * Inverts an effect - currently not for Sequential composite effects
   *
   */
  public void invert() {
    assert (!(this instanceof SequentialCompositeEffect));
    inverted = !inverted;
  }
 
  /**
   * Returns a boolean value indicating if the effect has finished or not.
   *
   * @return True if the effect is finished; False otherwise
   */
  public boolean isFinished() {
    return effectFinished;
  }

  /**
   * Returns a boolean value to indicate if the effect is interruptable or not.
   *
   * @return True if the effect is interruptable; false otherwise.
   */
  public boolean isInterruptable() {
    return interruptable;
  }


  /**
   * Returns a boolean value to indicate if the effect is inverted.
   *
   * @return
   */

  public boolean isInverted() {
    return inverted;
  }

  /**
   * Called immediately after the animation is canceled. The default
   * implementation of this method calls {@link #onComplete()} only if the
   * animation has actually started running.
   *
   * This overrides the version in the Animate class to allow the inclusion of
   * the effectHandlers.
   *
   */
  @Override
  protected void onCancel() {
    // Removed call to super, see issues 82 and 83.
    // super.onCancel();
    if(paused) fireEvent(new EffectPausedEvent());
    else fireEvent(new EffectInterruptedEvent());
  }

  /**
   * Called immediately after the animation completes.
   *
   * This overrides the version in the Animate class to allow the inclusion of
   * the effectHandlers.
   */
  @Override
  protected void onComplete() {
    super.onComplete();
    effectFinished = true;
    fireEvent(new EffectCompletedEvent());
    if(looping){
      DeferredCommand.addCommand(new Command(){
        public void execute() {
          play(0.0, 1.0);         
        }
      });
    }
  }

  /**
   * Called immediately before the animation starts.
   *
   * This overrides the version in the Animate class to allow the inclusion of
   * the effectHandlers.
   *
   */
  @Override
  protected void onStart() {
    // Issue 95 means don't call super.onStart() as it causes any resumeBackwards() calls to flash to position 0.0 first
    // super.onStart();
    // In GWT1.6 super.onStart() does nothing else but call onUpdate(0.0);
    effectFinished = false;
    effectStarted = true;
    fireEvent(new EffectStartingEvent());
  }

  /**
   * Called when the animation should be updated.
   *
   * The value of progress is between 0.0 and 1.0 inclusively (unless you
   * override the {@link #interpolate(double)} method to provide a wider range
   * of values). You can override {@link #onStart()} and {@link #onComplete()}
   * to perform setup and tear down procedures.
   *
   * Note that onUpdate events are now triggered by a call to the interpolate
   * method whereas before they were through the direct update methods - this
   * needs to be done since the update method in GWT Animation is private.
   *
   */
  @Override
  protected void onUpdate(double progress) {
    currentEffectPosition = progress;
  }
 
  public void pause(){
    this.cancel();
  }

  /**
   * The play method - defaults to playing effect from position 0.0 to 1.0.
   * Issue #66 introduced the isInitialised check.
   */
  public void play() {
    if(!isInitialised){
      //setUpEffect();
      init();
      isInitialised = true;
    }
    double timeRatio = this.requestedEffectEnd - this.requestedEffectStart;
    if (timeRatio<0) timeRatio = -timeRatio;
    run((int) ((effectLengthSeconds * 1000) * timeRatio));
  }

  /**
   * The play method - allows specific definition of start and end positions.
   *
   * Note that the portion of effect selected will run at the same tempo as the
   * whole effect, but the firing of any postEffect handlers will not occur
   * until the whole duration is complete.
   *
   * For example, an effect with duration 2 seconds, but calling play(0.0, 0.5)
   * will appear to viewer to complete in 1 second, but will not fire any
   * postEvents until after 2 seconds.
   *
   * @param start The requested start position.
   * @param end The requested end position.
   */
  public void play(double start, double end) {
    // If the start point is greater than the end point, then we need to play
    // the effect in reverse
    // But, note that this is not the same as inverting the effect.
    if (start > end)
      backwards = true;
    else
      backwards = false;
    // Store the start and end positions.
    requestedEffectStart = start;
    requestedEffectEnd = end;
    // Play the effect as normal
    play();
  }

  public void play(final double start, final double end, int afterDelay){
      startDelay = new Timer(){
        @Override
        public void run() {
          play(start, end);
        }
      };
      startDelay.schedule(afterDelay);
  }

  public void play(int afterDelay){
      startDelay = new Timer(){
        @Override
        public void run() {
          play();
        }
      };
      startDelay.schedule(afterDelay);
  }
 
  protected void registerEffectElement(){
    //if(effectElement==null)effectElement = thePanel.getPanelWidget().getElement();
    // Enhancement 104
    if(effectElements.isEmpty()){
      Element el = thePanel.getWidget().getElement();
      effectElements.add(el);
    }
  }
 
  public void remove() {
    cancel();
    tearDownEffect();
  }

  /**
   * Allow effect to be removed if it was added directly to Element
   * (if added via NEffectPanel it must be removed that way).
   *
   * See issue #77
   *
   */
  public void removeEffect(){
    removeEffect(true);
  }

  /**
   * Allow effect to be removed if it was added directly to Elements
   * It is removed per element.
   * (if added via NEffectPanel it must be removed that way).
   *
   * @param undoEffect set to true to call tearDown which may undo the effect; or false to do nothing
   */
  public void removeEffect(boolean undoEffect){
    assert(thePanel == null);
    if(undoEffect) tearDownEffect();
    effectElements = null;
  }

  /** Reset the effect */
  public void reset() {
    if (thePanel != null)
      onUpdate(0.0);
  }

  public void resumeBackwards() {
    paused = false;
    this.play(getProgress(), 0.0);
    fireEvent(new EffectResumedEvent());
  }

  public void resumeBackwards(double end) {
    paused = false;
    this.play(getProgress(), end);
    fireEvent(new EffectResumedEvent());
  }

  public void resumeForwards() {
    paused = false;
    this.play(getProgress(), 1.0);
    fireEvent(new EffectResumedEvent());
  }

  public void resumeForwards(double end) {
    paused = false;
    this.play(getProgress(), end);
    fireEvent(new EffectResumedEvent());
  }

  /**
   * Set the effect length.
   *
   * @param new_EffectLengthSeconds
   */
  public void setDuration(double new_EffectLengthSeconds) {
    effectLengthSeconds = new_EffectLengthSeconds;
  }
 
  /**
   * Sets the element for which an effect is applied
   * If it is currently applied to any elements, then those elements are removed from the effect after
   * enhancement 104 allowed multiple elements to be managed.
   * See addEffectElement() if you wish to add elements to be managed by this effect
   * @param newEl
   */
  public void setEffectElement(Element newEl){
    if (thePanel!=null)throw new RuntimeException("Trying to change effectElement when an EffectPanel is in place");
    effectElements = new Vector<Element>();
    effectElements.add(newEl);
    isInitialised = false;
  }

  /**
   * Allow external user to specify position within an effect
   *
   * @param progress the position to be set.
   */
  public void setPosition(double progress) {
    if (!this.isInitialised) {
      init();
      isInitialised = true;
    }
    double val = interpolate(progress);
    onUpdate(val);
    if (val == 0) fireEvent(new EffectStartingEvent())
    else if (val == 0) fireEvent(new EffectCompletedEvent());
    else fireEvent(new EffectSteppingEvent());
  }
  /*
   *
   */
  public void setTransitionType(TransitionPhysics newTransition) {
    this.transition = newTransition;
    if(chainedEffects!=null){
      for (Iterator<NEffect> it = chainedEffects.iterator(); it.hasNext();) {
        NEffect theEffect = it.next();
        if(!theEffect.equals(this))theEffect.setTransitionType(newTransition);
      }
    }
  }
 
  /**
   * Gets the layout definition that IE requires to use filters.
   * Only has meaning for IE.
   */
  public String getIELayoutDefinition(){
    return EffectImplementation.getIELayoutDefinition();
  }
 
  /**
   * Sets the layout definition that IE requires to use filters.
   * Is only required for IE.
   */
  public void setIELayoutDefinition(String id, String val){
    EffectImplementation.setIELayoutDefinition(id,val);
  }
 
  /**
   * Method that is called to set the effect up. It is abstract here since we
   * are not defining an actual effect; but it is expected to be implemented by
   * implementations. (For example a Show effect may wish to have this method
   * hide the component before starting).
   */
  public abstract void setUpEffect();
 
  /**
   * Method that is called when the effect is removed from a panel. Usually this
   * will not do anything, but for sure the Move effect needs to fix the style
   * properties that it sets up.
   */
  public abstract void tearDownEffect();
}
TOP

Related Classes of org.adamtacy.client.ui.effects.NEffect

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.