2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * Copyright (C) 2010-2015 Martin
19 package com
.googlecode
.lanterna
.gui2
;
21 import com
.googlecode
.lanterna
.TerminalSize
;
23 import java
.lang
.ref
.WeakReference
;
27 * This is a special label that contains not just a single text to display but a number of frames that are cycled
28 * through. The class will manage a timer on its own and ensure the label is updated and redrawn. There is a static
29 * helper method available to create the classic "spinning bar": {@code createClassicSpinningLine()}
31 public class AnimatedLabel
extends Label
{
32 private static Timer TIMER
= null;
33 private static final WeakHashMap
<AnimatedLabel
, TimerTask
> SCHEDULED_TASKS
= new WeakHashMap
<AnimatedLabel
, TimerTask
>();
36 * Creates a classic spinning bar which can be used to signal to the user that an operation in is process.
37 * @return {@code AnimatedLabel} instance which is setup to show a spinning bar
39 public static AnimatedLabel
createClassicSpinningLine() {
40 return createClassicSpinningLine(150);
44 * Creates a classic spinning bar which can be used to signal to the user that an operation in is process.
45 * @param speed Delay in between each frame
46 * @return {@code AnimatedLabel} instance which is setup to show a spinning bar
48 public static AnimatedLabel
createClassicSpinningLine(int speed
) {
49 AnimatedLabel animatedLabel
= new AnimatedLabel("-");
50 animatedLabel
.addFrame("\\");
51 animatedLabel
.addFrame("|");
52 animatedLabel
.addFrame("/");
53 animatedLabel
.startAnimation(speed
);
57 private final List
<String
[]> frames
;
58 private TerminalSize combinedMaximumPreferredSize
;
59 private int currentFrame
;
62 * Creates a new animated label, initially set to one frame. You will need to add more frames and call
63 * {@code startAnimation()} for this to start moving.
65 * @param firstFrameText The content of the label at the first frame
67 public AnimatedLabel(String firstFrameText
) {
68 super(firstFrameText
);
69 frames
= new ArrayList
<String
[]>();
71 combinedMaximumPreferredSize
= TerminalSize
.ZERO
;
73 String
[] lines
= splitIntoMultipleLines(firstFrameText
);
75 ensurePreferredSize(lines
);
79 * Adds one more frame at the end of the list of frames
80 * @param text Text to use for the label at this frame
82 public synchronized void addFrame(String text
) {
83 String
[] lines
= splitIntoMultipleLines(text
);
85 ensurePreferredSize(lines
);
88 private void ensurePreferredSize(String
[] lines
) {
89 combinedMaximumPreferredSize
= combinedMaximumPreferredSize
.max(getBounds(lines
, combinedMaximumPreferredSize
));
93 * Advances the animated label to the next frame. You normally don't need to call this manually as it will be done
94 * by the animation thread.
96 public synchronized void nextFrame() {
98 if(currentFrame
>= frames
.size()) {
101 super.setLines(frames
.get(currentFrame
));
106 public void onRemoved(Container container
) {
111 * Starts the animation thread which will periodically call {@code nextFrame()} at the interval specified by the
112 * {@code millisecondsPerFrame} parameter. After all frames have been cycled through, it will start over from the
114 * @param millisecondsPerFrame The interval in between every frame
116 public synchronized void startAnimation(long millisecondsPerFrame
) {
118 TIMER
= new Timer("AnimatedLabel");
120 AnimationTimerTask animationTimerTask
= new AnimationTimerTask(this);
121 SCHEDULED_TASKS
.put(this, animationTimerTask
);
122 TIMER
.scheduleAtFixedRate(animationTimerTask
, millisecondsPerFrame
, millisecondsPerFrame
);
126 * Halts the animation thread and the label will stop at whatever was the current frame at the time when this was
129 public synchronized void stopAnimation() {
130 removeTaskFromTimer(this);
133 private static synchronized void removeTaskFromTimer(AnimatedLabel animatedLabel
) {
134 SCHEDULED_TASKS
.get(animatedLabel
).cancel();
135 SCHEDULED_TASKS
.remove(animatedLabel
);
139 private static synchronized void canCloseTimer() {
140 if(SCHEDULED_TASKS
.isEmpty()) {
146 private static class AnimationTimerTask
extends TimerTask
{
147 private final WeakReference
<AnimatedLabel
> labelRef
;
149 private AnimationTimerTask(AnimatedLabel label
) {
150 this.labelRef
= new WeakReference
<AnimatedLabel
>(label
);
155 AnimatedLabel animatedLabel
= labelRef
.get();
156 if(animatedLabel
== null) {
161 if(animatedLabel
.getBasePane() == null) {
162 animatedLabel
.stopAnimation();
165 animatedLabel
.nextFrame();