001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.ui.fx;
021 
022 import js.Id;
023 import js.Initializer;
024 import js.Js;
025 import js.ObjectLike;
026 import js.Ref;
027 import jsx.Interval;
028 import jsx.core.Variables;
029 import jsx.motion.Motion;
030 import jsx.motion.reg.Uniform;
031 import jsx.ui.Component;
032 import jsx.ui.Widget;
033 import jsx.ui.fx.event.Animation;
034 import jsx.ui.fx.event.OnAnimation;
035 
036 /**
037  * <p>An abstract base class for widgets providing visual effects abilities to
038  * the underlying components.</p>
039  * <p>Note that an {@link Effects} is a {@link Widget} that listens to the underlying
040  * component for {@link Animation} events.</p>
041  * 
042  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
043  */
044 public abstract class Effects extends Widget implements OnAnimation
045 {
046     /**
047      * <p>A global identifier for a configurable property of a {@link Effects} widget.</p>
048      * <p>The identified configurable property of a {@link Effects} widget is a reference 
049      * to an integer number that defines the total discrete but successive frames for 
050      * animation.</p>
051      * @since 1.0
052      */
053     public final static Id<Integer> FRAMES = new Id<Integer>();
054     /**
055      * <p>A global identifier for a configurable property of a {@link Effects} widget.</p>
056      * <p>The identified configurable property of a {@link Effects} widget is a reference 
057      * to an integer number that defines the total duration of an animation in milliseconds.</p>
058      * @since 1.0
059      */
060     public final static Id<Integer> PERIOD = new Id<Integer>();
061     /**
062      * <p>A global identifier for a configurable property of a {@link Effects} widget.</p>
063      * <p>The identified configurable property of a {@link Effects} widget is a reference 
064      * to a {@link Motion} object that defines the motion features for animation.</p>
065      * @since 1.0
066      */
067     public final static Id<Motion>  MOTION = new Id<Motion>();
068 
069     /**
070      * <p>Typically constructs a effects widget.</p>
071      * <p>This constructor invokes super constructor and configures the widget.</p>
072      * @param ini A configuring object.
073      * @since 1.0
074      */
075     protected Effects(ObjectLike ini) {
076         super(ini);
077         unwrap().addListener(Animation.class, this);
078         if (Variables.undefined(getPeriod(this))) {
079             ini(this).var(PERIOD, 1000);
080         }
081         if (Variables.undefined(getFrames(this))) {
082             ini(this).var(FRAMES, getPeriod(this) * 50 / 1000 + 1);
083         }
084         if (Js.not(getMotion(this))) {
085             ini(this).var(MOTION, new Uniform());
086         }
087         getMotion(this).init(getFrames(this));
088         unwrap().addListener(Animation.class, this);
089     }
090 
091     /**
092      * <p>Constructs a effects widget over a component.</p>
093      * <p>This constructor invokes {@link #Effects(ObjectLike)} with the given
094      * component configured.</p>
095      * @param e A component to have the visual effects.
096      * @since 1.0
097      */
098     protected Effects(Component e) {
099         this(new Initializer().set(COMPONENT, e).var());
100     }
101 
102     /**
103      * <p>Returns the total frames defined with a visual effects widget.</p>
104      * <p>This method simply return the value of the {@link #FRAMES} property of 
105      * the widget.</p>
106      * @param fx A visual effects widget.
107      * @return The number of the total frames.
108      * @since 1.0
109      */
110     public static final Integer getFrames(Effects fx) {
111         return ini(fx).var(FRAMES);
112     }
113 
114     /**
115      * <p>Returns the duration in milliseconds defined with a visual effects widget.</p>
116      * <p>This method simply return the value of the {@link #PERIOD} property of 
117      * the widget.</p>
118      * @param fx A visual effects widget.
119      * @return The milliseconds of duration.
120      * @since 1.0
121      */
122     public static final Integer getPeriod(Effects fx) {
123         return ini(fx).var(PERIOD);
124     }
125 
126     /**
127      * <p>Returns the {@link Motion} object defined with a visual effects widget.</p>
128      * <p>This method simply return the value of the {@link #MOTION} property of 
129      * the widget.</p>
130      * @param fx A visual effects widget.
131      * @return The {@link Motion} object.
132      * @since 1.0
133      */
134     public static final Motion getMotion(Effects fx) {
135         return ini(fx).var(MOTION);
136     }
137 
138     /**
139      * <p>Defines an action for the given frame.</p>
140      * @param i An index of the current frame ranging from 0 and the total number 
141      * of frames obtained by {@link #getFrames(Effects)}.
142      * @since 1.0
143      */
144     protected abstract void frame(int i);
145 
146     /**
147      * <p>Finishes an animation with a given style object applied to the widget.</p>
148      * <p>Subclasses overrides or invokes this method to provide different visual effects.</p>
149      * @param style The final style of the widget.
150      * @since 1.0
151      */
152     protected void stop(ObjectLike style) {
153         if (Js.be(style)) {
154             Component.applyStyle(unwrap(), style);
155         }
156     }
157 
158     /**
159      * <p>Animates the widget with a given final style.</p>
160      * <p>Subclasses overrides or invokes this method to provide different visual effects.</p>
161      * @param style The final style of the widget.
162      * @since 1.0
163      */
164     protected final void animate(final ObjectLike style) {
165         final Ref<Integer> fid = new Ref<Integer>(0);
166         new Interval() {
167             @Override
168             public void run() {
169                 frame(fid.value);
170                 if (fid.value >= getFrames(Effects.this)) {
171                     stop(style);
172                     clear();
173                 }
174                 fid.value += 1;
175             }
176         }.set(getPeriod(this) / getFrames(this));
177     }
178 }