/*
 * This file is part of lanterna (http://code.google.com/p/lanterna/).
 * 
 * lanterna is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Copyright (C) 2010-2020 Martin Berglund
 */
package com.googlecode.lanterna.gui2;

import com.googlecode.lanterna.TerminalSize;

import java.lang.ref.WeakReference;
import java.util.*;

This is a special label that contains not just a single text to display but a number of frames that are cycled through. The class will manage a timer on its own and ensure the label is updated and redrawn. There is a static helper method available to create the classic "spinning bar": createClassicSpinningLine()
/** * This is a special label that contains not just a single text to display but a number of frames that are cycled * through. The class will manage a timer on its own and ensure the label is updated and redrawn. There is a static * helper method available to create the classic "spinning bar": {@code createClassicSpinningLine()} */
public class AnimatedLabel extends Label { private static Timer TIMER = null; private static final WeakHashMap<AnimatedLabel, TimerTask> SCHEDULED_TASKS = new WeakHashMap<AnimatedLabel, TimerTask>();
Creates a classic spinning bar which can be used to signal to the user that an operation in is process.
Returns:AnimatedLabel instance which is setup to show a spinning bar
/** * Creates a classic spinning bar which can be used to signal to the user that an operation in is process. * @return {@code AnimatedLabel} instance which is setup to show a spinning bar */
public static AnimatedLabel createClassicSpinningLine() { return createClassicSpinningLine(150); }
Creates a classic spinning bar which can be used to signal to the user that an operation in is process.
Params:
  • speed – Delay in between each frame
Returns:AnimatedLabel instance which is setup to show a spinning bar
/** * Creates a classic spinning bar which can be used to signal to the user that an operation in is process. * @param speed Delay in between each frame * @return {@code AnimatedLabel} instance which is setup to show a spinning bar */
public static AnimatedLabel createClassicSpinningLine(int speed) { AnimatedLabel animatedLabel = new AnimatedLabel("-"); animatedLabel.addFrame("\\"); animatedLabel.addFrame("|"); animatedLabel.addFrame("/"); animatedLabel.startAnimation(speed); return animatedLabel; } private final List<String[]> frames; private TerminalSize combinedMaximumPreferredSize; private int currentFrame;
Creates a new animated label, initially set to one frame. You will need to add more frames and call startAnimation() for this to start moving.
Params:
  • firstFrameText – The content of the label at the first frame
/** * Creates a new animated label, initially set to one frame. You will need to add more frames and call * {@code startAnimation()} for this to start moving. * * @param firstFrameText The content of the label at the first frame */
public AnimatedLabel(String firstFrameText) { super(firstFrameText); frames = new ArrayList<String[]>(); currentFrame = 0; combinedMaximumPreferredSize = TerminalSize.ZERO; String[] lines = splitIntoMultipleLines(firstFrameText); frames.add(lines); ensurePreferredSize(lines); } @Override protected synchronized TerminalSize calculatePreferredSize() { return super.calculatePreferredSize().max(combinedMaximumPreferredSize); }
Adds one more frame at the end of the list of frames
Params:
  • text – Text to use for the label at this frame
Returns:Itself
/** * Adds one more frame at the end of the list of frames * @param text Text to use for the label at this frame * @return Itself */
public synchronized AnimatedLabel addFrame(String text) { String[] lines = splitIntoMultipleLines(text); frames.add(lines); ensurePreferredSize(lines); return this; } private void ensurePreferredSize(String[] lines) { combinedMaximumPreferredSize = combinedMaximumPreferredSize.max(getBounds(lines, combinedMaximumPreferredSize)); }
Advances the animated label to the next frame. You normally don't need to call this manually as it will be done by the animation thread.
/** * Advances the animated label to the next frame. You normally don't need to call this manually as it will be done * by the animation thread. */
public synchronized void nextFrame() { currentFrame++; if(currentFrame >= frames.size()) { currentFrame = 0; } super.setLines(frames.get(currentFrame)); invalidate(); } @Override public void onRemoved(Container container) { stopAnimation(); }
Starts the animation thread which will periodically call nextFrame() at the interval specified by the millisecondsPerFrame parameter. After all frames have been cycled through, it will start over from the first frame again.
Params:
  • millisecondsPerFrame – The interval in between every frame
Returns:Itself
/** * Starts the animation thread which will periodically call {@code nextFrame()} at the interval specified by the * {@code millisecondsPerFrame} parameter. After all frames have been cycled through, it will start over from the * first frame again. * @param millisecondsPerFrame The interval in between every frame * @return Itself */
public synchronized AnimatedLabel startAnimation(long millisecondsPerFrame) { if(TIMER == null) { TIMER = new Timer("AnimatedLabel"); } AnimationTimerTask animationTimerTask = new AnimationTimerTask(this); SCHEDULED_TASKS.put(this, animationTimerTask); TIMER.scheduleAtFixedRate(animationTimerTask, millisecondsPerFrame, millisecondsPerFrame); return this; }
Halts the animation thread and the label will stop at whatever was the current frame at the time when this was called
Returns:Itself
/** * Halts the animation thread and the label will stop at whatever was the current frame at the time when this was * called * @return Itself */
public synchronized AnimatedLabel stopAnimation() { removeTaskFromTimer(this); return this; } private static synchronized void removeTaskFromTimer(AnimatedLabel animatedLabel) { SCHEDULED_TASKS.get(animatedLabel).cancel(); SCHEDULED_TASKS.remove(animatedLabel); canCloseTimer(); } private static synchronized void canCloseTimer() { if(SCHEDULED_TASKS.isEmpty()) { TIMER.cancel(); TIMER = null; } } private static class AnimationTimerTask extends TimerTask { private final WeakReference<AnimatedLabel> labelRef; private AnimationTimerTask(AnimatedLabel label) { this.labelRef = new WeakReference<AnimatedLabel>(label); } @Override public void run() { AnimatedLabel animatedLabel = labelRef.get(); if(animatedLabel == null) { cancel(); canCloseTimer(); } else { if(animatedLabel.getBasePane() == null) { animatedLabel.stopAnimation(); } else { animatedLabel.nextFrame(); } } } } }