/********************************************************************************* * 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.ui; import totalcross.sys.*; import totalcross.ui.gfx.*; import totalcross.ui.image.*; /** Spinner is a control that shows an image indicating that something is running in * the background. * * To start the spin call the start method, and to stop it call the stop method. * * If you try to run a spinner inside a tight loop, be sure to call <code>spinner.update()</code> or the spinner will not be * updated. Without this, it will work in Java but not on devices. * * @since TotalCross 1.3 */ public class Spinner extends Control implements Runnable { /** Used in the type field */ public static final int IPHONE = 1; /** Used in the type field */ public static final int ANDROID = 2; /** Used in the type field */ public static final int SYNC = 3; /** Defines the type of spinner for all instances. Defaults for IPHONE when running in iPhone and * ANDROID for all other platforms. */ public static int spinnerType = Settings.isIOS() ? IPHONE : ANDROID; private static Image[] loaded = new Image[4]; private static String[] files = { null, "totalcross/res/spinner_iphone.gif", "totalcross/res/spinner_android.gif", "totalcross/res/spinner_sync.gif", }; private boolean running; private Image anim,anim0; /** Creates a spinner with the defined spinnerType. */ public Spinner() { this(spinnerType); } /** Creates a spinner of the given type. */ public Spinner(int type) { setType(type); } /** Changes the Spinner to one of the predefined types. */ public void setType(int t) { if (t < IPHONE || t > SYNC) throw new IllegalArgumentException("Invalid type"); try { anim0 = loaded[t] == null ? loaded[t] = new Image(files[t]) : loaded[t]; anim = null; } catch (Exception e) { if (Settings.onJavaSE) e.printStackTrace(); } } /** Creates a spinner from an animated GIF. * You can download additional animations from: <a href='http://preloaders.net/en'>here</a>. * Change only the given settings: * <ul> * <li> Image type: GIF * <li> Transparent background: Yes * <li> Foreground color: FFFFFF if the animation is only black, 000000 if it has fade. * <li> Background color: 000000 * <li> Keep size 128 x 128 * </ul> * Then press Generate preloader and download the gif file that will appear at the right pane. * If the spinner is moving counterclockwise, you can make it go clickwise by changing also, under the Advanced options: * <ul> * <li> Flip image: Hor * <li> Reverse animation: Yes * </ul> * The image is colorized with the foreground color. * If it appears not filled, try selecting the "Invert colors" option, and use 000000 as foreground color. */ public Spinner(Image anim) { this.anim0 = anim; if (UIColors.spinnerBack != -1) backColor = UIColors.spinnerBack; foreColor = UIColors.spinnerFore; } /** Changes the gif image of this Spinner */ public void setImage(Image anim) { this.anim0 = anim; this.anim = null; } public void onBoundsChanged(boolean screenChanged) { anim = null; } public void onColorsChanged(boolean changed) { anim = null; } public void onPaint(Graphics g) { if (!Settings.isOpenGL) { g.backColor = backColor; g.fillRect(0,0,width,height); } if (anim == null) checkAnim(); if (anim != null) g.drawImage(anim, (width-anim.getWidth())/2,(height-anim.getHeight())/2); } private void checkAnim() { try { anim = anim0.smoothScaledFixedAspectRatio(width < height ? width : height,true); anim.applyColor2(getForeColor() | 0xAA000000); } catch (Exception e) {anim = null;} } /** Starts the spinning thread. */ public void start() { if (running) return; running = true; new Thread(this).start(); } /** Stops the spinning thread. */ public void stop() { running = false; } /** Returns if the spin is running. */ public boolean isRunning() { return running; } private void step() { if (anim == null) checkAnim(); if (getParentWindow() == Window.topMost && anim != null) // don't update if we loose focus { anim.nextFrame(); safeRepaintNow(); } } public void run() { while (running) { step(); Vm.sleep(anim != null ? 80 : 120); // with safeSleep, the vm starts to behave slowly and strangely } } int last; /** Updates the spinner; call this when using the spinner inside a loop. */ public void update() { int now = Vm.getTimeStamp(); if ((now - last) > (anim != null ? 80 : 120)) // prevents calling pumpEvents too fast { step(); if (!MainWindow.isMainThread()) Vm.sleep(1); last = now; } } }