source: trunk/src/org/expeditee/gui/PopupManager.java@ 1102

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

File size: 7.4 KB
Line 
1/**
2 * PopupManager.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.gui;
20
21import java.util.LinkedList;
22import java.util.List;
23
24import org.expeditee.core.Point;
25import org.expeditee.core.bounds.AxisAlignedBoxBounds;
26import org.expeditee.gio.EcosystemManager;
27import org.expeditee.gio.EcosystemManager.Ecosystem;
28
29/**
30 * A centralised container for all custom pop-ups in expeditee.
31 *
32 * @author Brook Novak
33 */
34public final class PopupManager implements DisplayObserver
35{
36 /** Singleton */
37 private static PopupManager _instance = null;
38
39 /**
40 * @return The singleton instance.
41 */
42 public static PopupManager getInstance()
43 {
44 // Only valid for Swing ecosystems
45 if (EcosystemManager.getType() != Ecosystem.Swing) return null;
46
47 // Construct the instance
48 if (_instance == null) _instance = new PopupManager();
49
50 return _instance;
51 }
52
53 /** Singleton */
54 private PopupManager()
55 {
56 }
57
58 private LinkedList<Popup> _popups = new LinkedList<Popup>();
59
60 /** Time its takes for a maximise/minimise to animate, in milliseconds. */
61 //private final int ANIMATION_DURATION = 180;
62 /** in milliseconds. TODO: Expand. cts16 */
63 //private final int ANIMATION_RATE = 30;
64
65 public void add(Popup p)
66 {
67 if (p == null) return;
68
69 synchronized(_popups) {
70 if (!_popups.contains(p)) _popups.add(p);
71 }
72 }
73
74 public void remove(Popup p)
75 {
76 if (p == null) return;
77
78 synchronized(_popups) {
79 if (_popups.contains(p)) _popups.remove(p);
80 }
81 }
82
83 public List<Popup> getPopups()
84 {
85 synchronized(_popups) {
86 return new LinkedList<Popup>(_popups);
87 }
88 }
89
90 /**
91 * Determines whether a given point is over a pop-up.
92 *
93 * @param p
94 *
95 * @return True if p is over a pop-up
96 *
97 * @throws NullPointerException
98 * If p is null
99 */
100 public boolean isPointOverPopup(Point p)
101 {
102 if (p == null) throw new NullPointerException("p");
103
104 synchronized(_popups) {
105 for (Popup popup : _popups) {
106 if (popup.getBounds().contains(p)) return true;
107 }
108 }
109
110 return false;
111 }
112
113 /**
114 * Use this instead of isVisible to determine if the popup is showing or not,
115 * since it considers animation.
116 *
117 * @return
118 * True if this popup is showing. <b>IMPORTANT:</b> This includes
119 * if the popup is not yet visible, but in an animation sequence for showing...
120 *
121 * @throws NullPointerException
122 * If p is null
123 */
124 public boolean isShowing(Popup p)
125 {
126 if (p == null) throw new NullPointerException("p");
127
128 synchronized(_popups) {
129 return _popups.contains(p) && p.isShowing();
130 }
131 }
132
133 /**
134 * @return
135 * True if a pop-up is showing. False otherwise.
136 */
137 public boolean isAnyPopupsShowing()
138 {
139 synchronized(_popups) {
140 return !_popups.isEmpty();
141 }
142 }
143
144 public void hideAutoHidePopups()
145 {
146 synchronized(_popups) {
147 for (Popup p : _popups) {
148 if (p.doesAutoHide()) p.hide();
149 }
150 }
151 }
152
153 /**
154 * @return
155 * True if the mouse click event for going back a frame should be consumed
156 * due to a pop-up requesting this event to be consumed currently showing.
157 */
158 public boolean shouldConsumeBackClick()
159 {
160 synchronized(_popups) {
161 for (Popup p : _popups) {
162 if (p.shouldConsumeBackClick()) return true;
163 }
164 }
165
166 return false;
167 }
168
169 public void frameChanged()
170 {
171 }
172
173 /**
174 * Paints current popup animations to the expeditee browser content pane.
175 */
176 public void paint()
177 {
178 synchronized (_popups) {
179 for (Popup popup : _popups) {
180 popup.update();
181 popup.paint();
182 }
183 }
184 }
185
186 /**
187 * Provides animations for a popup when hiding or when showing.
188 *
189 * Note that {@link PopupAnimator#paint(Graphics)} and {@link PopupAnimator#update(float)}
190 * will always be invoked at seperate times - so do not have to worry about thread-safety.
191 *
192 * These should only be used once... one per popup at a time.
193 *
194 * @author Brook Novak
195 */
196 public static abstract class PopupAnimator
197 {
198 /** True if animating to show the popup, false if to hide. */
199 private boolean _showing = true;
200
201 /** Whether the animation is currently paused. */
202 private boolean _paused = true;
203
204 private long _startTime;
205 private long _duration;
206 private long _animationCurrentTime;
207
208 public PopupAnimator()
209 {
210 _animationCurrentTime = _startTime = System.currentTimeMillis();
211 _duration = getDurationMillis();
212 }
213
214 /**
215 * @return False if the animation has finished, true if further updates are required.
216 */
217 public boolean update()
218 {
219 long currentTime = System.currentTimeMillis();
220
221 if (currentTime > _startTime + _duration) {
222 currentTime = _startTime + _duration;
223 }
224
225 // If paused, make sure elapsed time stays constant
226 if (_paused) {
227 _startTime += currentTime - _animationCurrentTime;
228 }
229
230 _animationCurrentTime = currentTime;
231
232 return !isFinished();
233 }
234
235 public void hide()
236 {
237 if (!_showing) return;
238
239 _showing = false;
240 complementAnimationProgress();
241 }
242
243 public void show()
244 {
245 if (_showing) return;
246
247 _showing = true;
248 complementAnimationProgress();
249 }
250
251 public boolean isShowing()
252 {
253 return _showing;
254 }
255
256 public void start()
257 {
258 _animationCurrentTime = _startTime = System.currentTimeMillis();
259 unpause();
260 }
261
262 public void pause()
263 {
264 pause(true);
265 }
266
267 public void unpause()
268 {
269 pause(false);
270 }
271
272 public void pause(boolean pause)
273 {
274 _paused = pause;
275 }
276
277 private void complementAnimationProgress()
278 {
279 _startTime = _animationCurrentTime - ((_startTime + _duration) - _animationCurrentTime);
280 }
281
282 public float getProgress()
283 {
284 return (_animationCurrentTime - _startTime) / ((float) _duration);
285 }
286
287 public boolean isFinished()
288 {
289 return _animationCurrentTime != _startTime + _duration;
290 }
291
292 public abstract long getDurationMillis();
293
294 public abstract AxisAlignedBoxBounds getAnimatedBounds(AxisAlignedBoxBounds fullBounds);
295
296 }
297
298 public static class ExpandShrinkAnimator extends PopupAnimator
299 {
300 public static final long DURATION_MS = 2000;
301
302 public AxisAlignedBoxBounds _initialBounds = null;
303
304 public ExpandShrinkAnimator()
305 {
306 }
307
308 public ExpandShrinkAnimator(AxisAlignedBoxBounds initialBounds)
309 {
310 setInitialBounds(initialBounds);
311 }
312
313 public void setInitialBounds(AxisAlignedBoxBounds initialBounds)
314 {
315 _initialBounds = initialBounds;
316 }
317
318 @Override
319 public long getDurationMillis() { return DURATION_MS; }
320
321 @Override
322 public AxisAlignedBoxBounds getAnimatedBounds(AxisAlignedBoxBounds fullBounds)
323 {
324 if (_initialBounds == null) {
325 setInitialBounds(fullBounds.clone());
326 _initialBounds.getTopLeft().y += _initialBounds.getSize().height;
327 _initialBounds.getSize().height = 0;
328 }
329
330 float percentShown = getProgress();
331 if (!isShowing()) percentShown = 1 - percentShown;
332
333 return AxisAlignedBoxBounds.lerp(_initialBounds, fullBounds, percentShown);
334 }
335 }
336}
Note: See TracBrowser for help on using the repository browser.