/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine 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. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.game; import totalcross.sys.*; import totalcross.ui.image.*; import totalcross.ui.event.*; /** * An animated button control. * <br> * This control displays an animated button which can take <code>'S'</code> different states and each state is fades in or out in <code>'F'</code> * frames. <code>'S'</code> and <code>'F'</code> represent the two first constructor arguments. The frames of this special animation have to be * ordered to be supported by this class. The states are numbered from <code>0</code> to <code>'S'-1</code> and the frames order is the following * depending on the layoutType value:<br> * * <pre> * FADE_OUT_LAYOUT : S0F0,S0F1,S0F2,S1F0,S1F1,S1F2,S2F0,S2F1,S2F2<br> * FADE_IN_LAYOUT : S0F2,S0F1,S0F0,S1F2,S1F1,S1F0,S2F2,S2F1,S2F0<br> * FADE_OUT_IN_LAYOUT : S0F0,S0F1,S1F1,S1F0,S1F1,S2F1,S2F0,S2F1,S0F1<br> * </pre> * * where <code>S</code> stands for state, <code>F</code> for frame; and where <code>S0F0</code>, <code>S1F0</code>, and <code>S2F0</code> are the * full states and the others are transition frames. Open the <code>onOff.bmp</code> in the Scape game sample and you will understand ;-) . * * @author Frank Diebolt * @author Guilherme Campos Hazan * @version 1.1 */ public class AnimatedButton extends Animation { /** * Defines the frames animation order. In the case of <code>S</code> states button of <code>F</code> frames per state, * <code>FADE_OUT_LAYOUT</code> means that the frames are a <code>S</code> set of <code>F</code> frames that are fading out the state, which means * the first frame of each set is the full state image. * In the <code>FADE_IN_LAYOUT</code> layout, it's the opposite, namely the last frame of each set represents the state ending position. * Finally the <code>FADE_OUT_IN_LAYOUT</code> is a mix of the two others, because inter-frames represent successively fading out from one state to * fading in to next state. */ public static final int FADE_OUT_LAYOUT = 0; /** * Frames fading in mode. * * @see #FADE_OUT_LAYOUT */ public static final int FADE_IN_LAYOUT = 1; /** * Frames fading out then fading in mode. * * @see #FADE_OUT_LAYOUT */ public static final int FADE_OUT_IN_LAYOUT = 2; /** current animated button state */ protected int state; protected int layoutType; protected int fadeInState; protected int framesPerState; protected int maxStates; protected int statesIndexes[]; private final static int IDLE = -1; /** * Animated button constructor. * * @param frames Button different states frames in multi-frame BMP format. * @param states Number of states of the button. * @param framesPerState Number of frames for each state. * @param layoutType <code>FADE_OUT_LAYOUT</code>, <code>FADE_IN_LAYOUT</code>, or <code>FADE_OUT_IN_LAYOUT</code>. * @param framePeriod Delay in milliseconds between two frames. * @throws ImageException If an internal method throws it. * * @see #FADE_OUT_LAYOUT * @see #FADE_IN_LAYOUT * @see #FADE_OUT_IN_LAYOUT */ public AnimatedButton(Image frames,int states,int framesPerState,int layoutType, int framePeriod) throws ImageException // fdie@341_2 { super(frames,states * framesPerState,framePeriod); this.framesPerState = framesPerState; this.layoutType = layoutType; this.maxStates = states; statesIndexes = new int[states]; for (int s=0; s<states; s++) statesIndexes[s] = (layoutType==FADE_IN_LAYOUT) ? ((s+1)*framesPerState)-1 : s*framesPerState; curFrame = statesIndexes[state=0]; fadeInState = IDLE; eventsMask = eventFinish; } /** * Sets the animated button state. * * @param state Value between <code>0</code> and <code>states-1</code>. */ public void setState(int state) { if (isPlaying) { stop(); fadeInState=IDLE; } this.state=state; curFrame=statesIndexes[state]; repaintNow(); } /** * Gets the animated button state. * * @return Value between <code>0</code> and <code>states-1</code>. */ public int getState() { return state; } /** * Animated button event handler. * * @param event The event being handled. */ public void onEvent(Event event) { switch (event.type) { case PenEvent.PEN_DOWN: if (fadeInState==IDLE) inc(((PenEvent)event).x >= (width>>1)); break; case KeyEvent.SPECIAL_KEY_PRESS: if (fadeInState==IDLE) { int key = ((KeyEvent)event).key; if (key == SpecialKeys.ACTION || key == SpecialKeys.ENTER) inc(true); } break; case AnimationEvent.FINISH: if (fadeInState!=IDLE) { state=fadeInState; fadeInState=IDLE; if (layoutType==FADE_OUT_IN_LAYOUT) { postPressedEvent(); return; } int dest=statesIndexes[state]; if (layoutType!=FADE_IN_LAYOUT) start(dest+framesPerState-1,dest,-1,1); else start(dest-framesPerState+1,dest,1,1); } break; case ControlEvent.PRESSED: postPressedEvent(); break; default: // pass timer events to the parent super.onEvent(event); } } /** * Increases/decreases the animated button state. * * @param up Boolean with a <code>true</code> value to increase the value; decrease otherwise. */ protected void inc(boolean up) { int dir=up ? 1:-1; int dest=(state+maxStates+dir) % maxStates; int src=statesIndexes[state]; if (layoutType==FADE_OUT_IN_LAYOUT) start(src,statesIndexes[dest],dir,1); else if (layoutType!=FADE_IN_LAYOUT) start(src,src+framesPerState-1,1,1); else start(src,src-framesPerState+1,-1,1); fadeInState=dest; } }