Changeset 1102 for trunk/src/org/expeditee/gui/PopupManager.java
- Timestamp:
- 05/10/18 16:04:51 (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/expeditee/gui/PopupManager.java
r919 r1102 19 19 package org.expeditee.gui; 20 20 21 import java.awt.BasicStroke;22 import java.awt.Color;23 import java.awt.Component;24 import java.awt.Graphics;25 import java.awt.Graphics2D;26 import java.awt.Point;27 import java.awt.Rectangle;28 import java.awt.Stroke;29 import java.awt.geom.Area;30 import java.util.HashMap;31 import java.util.HashSet;32 21 import java.util.LinkedList; 33 34 import javax.swing.JLayeredPane; 35 import javax.swing.SwingUtilities; 22 import java.util.List; 23 24 import org.expeditee.core.Point; 25 import org.expeditee.core.bounds.AxisAlignedBoxBounds; 26 import org.expeditee.gio.EcosystemManager; 27 import org.expeditee.gio.EcosystemManager.Ecosystem; 36 28 37 29 /** 38 * A centrali zed container for all custom popups in expeditee.30 * A centralised container for all custom pop-ups in expeditee. 39 31 * 40 32 * @author Brook Novak 41 33 */ 42 public final class PopupManager implements Display IOObserver {43 34 public final class PopupManager implements DisplayObserver 35 { 44 36 /** Singleton */ 45 private PopupManager() {} 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 } 46 52 47 53 /** Singleton */ 48 private static PopupManager _instance = new PopupManager(); 49 50 /** 51 * @return The singleton instance. 52 */ 53 public static PopupManager getInstance() { 54 return _instance; 55 } 56 57 // popup->invoker 58 private HashMap<Popup, Component> _popups = new HashMap<Popup, Component>(); 59 // quick ref to invokers 60 private HashSet<Component> _invokers = new HashSet<Component>(); 61 62 private LinkedList<AnimatedPopup> _animatingPopups = new LinkedList<AnimatedPopup>(); 63 private AnimationThread _animationThread = null; 64 65 private final int ANIMATION_DURATION = 180; // Tume its takes for a maximize . minimize to animate. In ms. 66 private final int ANIMATION_RATE = 30; // in ms 67 68 /** 69 * Determines whether a given point is over a popup. 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. 70 92 * 71 93 * @param p 72 94 * 73 * @return True if p is over a pop up95 * @return True if p is over a pop-up 74 96 * 75 97 * @throws NullPointerException 76 98 * If p is null 77 99 */ 78 public boolean isPointOverPopup(Point p) { 100 public boolean isPointOverPopup(Point p) 101 { 79 102 if (p == null) throw new NullPointerException("p"); 80 for (Popup pp : _popups.keySet()) { 81 if (pp.getBounds().contains(p)) { 82 return true; 103 104 synchronized(_popups) { 105 for (Popup popup : _popups) { 106 if (popup.getBounds().contains(p)) return true; 83 107 } 84 108 } 85 109 86 110 return false; 87 }88 89 /**90 * Tests a component to see if it is in invoker of an existing popup.91 *92 * @param c93 * Must not be null.94 *95 * @return96 * True if c is an invoker97 *98 * @throws NullPointerException99 * If c is null100 */101 public boolean isInvoker(Component c) {102 if (c == null) throw new NullPointerException("c");103 return _invokers.contains(c);104 }105 106 /**107 * Gets an invoker for a popup108 *109 * @param p110 * The popup to get the invoker for.111 *112 * @return113 * The invoker for the given popup.114 * Null if popup does not exist.115 */116 public Component getInvoker(Popup p) {117 if (p == null) throw new NullPointerException("p");118 return _popups.get(p);119 111 } 120 112 … … 130 122 * If p is null 131 123 */ 132 public boolean isShowing(Popup p) { 124 public boolean isShowing(Popup p) 125 { 133 126 if (p == null) throw new NullPointerException("p"); 134 return _popups.containsKey(p); 127 128 synchronized(_popups) { 129 return _popups.contains(p) && p.isShowing(); 130 } 135 131 } 136 132 137 133 /** 138 134 * @return 139 * True if a poup is showing. False otherwise. 140 */ 141 public boolean isAnyPopupsShowing () { 142 return !_popups.isEmpty(); 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 } 143 151 } 144 152 … … 146 154 * @return 147 155 * True if the mouse click event for going back a frame should be consumed 148 * Due to a popup requesting this event to be consumed currently showing. 149 */ 150 public boolean shouldConsumeBackClick() { 151 for (Popup p : _popups.keySet()) { 152 if (p.shouldConsumeBackClick()) 153 return true; 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 } 154 164 } 155 165 … … 157 167 } 158 168 159 /** 160 * Clears all popups from the browser that are autohidden. 161 * Stops animations. 162 */ 163 public void hideAutohidePopups() { 164 165 166 // Get rid of all animations that are not non-auto-hidden pops that are expanding 167 synchronized (_animatingPopups) { 168 169 LinkedList<AnimatedPopup> animationsToClear = new LinkedList<AnimatedPopup>(); 170 171 for (AnimatedPopup ap : _animatingPopups) { 172 if (!(ap.popup != null && !ap.popup.doesAutoHide())) { 173 animationsToClear.add(ap); 174 } 175 } 176 177 _animatingPopups.removeAll(animationsToClear); 178 179 } 180 181 LinkedList<Popup> popupsToClear = new LinkedList<Popup>(); 182 LinkedList<Component> invokersToClear = new LinkedList<Component>(); 183 184 // Get ride of the actual popups 185 for (Popup p : _popups.keySet()) { 186 if (p.doesAutoHide()) { 187 188 popupsToClear.add(p); 189 invokersToClear.add(_popups.get(p)); 190 191 p.invalidateAppearance(); 192 p.setVisible(false); 193 Browser._theBrowser.getLayeredPane().remove(p); 194 p.onHide(); 195 } 196 } 197 198 assert (popupsToClear.size() == invokersToClear.size()); 199 200 for (int i = 0; i < popupsToClear.size(); i++) { 201 _popups.remove(popupsToClear.get(i)); 202 _invokers.remove(invokersToClear.get(i)); 203 } 204 205 206 207 } 208 209 210 211 // public void hideAllPopups() { 212 // 213 // for (Popup p : _popups.keySet()) { 214 // invalidatePopup(p); 215 // p.setVisible(false); 216 // Browser._theBrowser.getLayeredPane().remove(p); 217 // p.onHide(); 218 // } 219 // _popups.clear(); 220 // _invokers.clear(); 221 // 222 // // Get rid of all animations 223 // synchronized (_animatingPopups) { 224 // _animatingPopups.clear(); 225 // } 226 // } 227 228 public void frameChanged() { 229 // Remove any popups that are showing on the current frame 230 hideAutohidePopups(); 231 } 232 233 /** 234 * Hides a popup - if not already hidden. 235 * 236 * @param p 237 * Must not be null. 238 * 239 * @throws NullPointerException 240 * If p is null 241 */ 242 public void hidePopup(Popup p) { 243 if (p == null) throw new NullPointerException("p"); 244 245 if (!isShowing(p) && (!p.isVisible() || p.getParent() == null)) return; 246 247 248 // Cancel any showing animations 249 synchronized (_animatingPopups) { 250 AnimatedPopup toRemove = null; 251 for (AnimatedPopup ap : _animatingPopups) { 252 if (ap.popup == p) { 253 toRemove = ap; 254 break; 255 } 256 } 257 if (toRemove != null) 258 _animatingPopups.remove(toRemove); 259 } 260 261 p.invalidateAppearance(); 262 p.setVisible(false); 263 Browser._theBrowser.getLayeredPane().remove(p); 264 Component invoker = _popups.remove(p); 265 if (invoker != null) _invokers.remove(invoker); 266 p.onHide(); 267 268 } 269 270 /** 271 * Hides a popup - with animation. - if not already hidden. 272 * 273 * @param p 274 * Must not be null. 275 * @param animator 276 * Must not be null. 277 * 278 * @throws NullPointerException 279 * If p or animator is null 280 * 281 */ 282 public void hidePopup(Popup p, PopupAnimator animator) { 283 284 if (p == null) throw new NullPointerException("p"); 285 if (animator == null) throw new NullPointerException("animator"); 286 287 if (!isShowing(p) && (!p.isVisible() || p.getParent() == null)) return; 288 289 hidePopup(p); 290 AnimatedPopup ap = new AnimatedPopup( 291 animator, 292 System.currentTimeMillis(), 293 null, 294 false, 295 p.getLocation()); 296 297 animator.starting(false, p.getBounds()); 298 299 synchronized (_animatingPopups) { 300 _animatingPopups.add(ap); 301 } 302 303 if (_animationThread == null || !_animationThread.isAlive() || _animationThread.willDie) { 304 _animationThread = new AnimationThread(); 305 _animationThread.start(); 306 } 307 } 308 309 310 311 /** 312 * Displays a popup at a specific location. 313 * 314 * @param p 315 * Must not be null. 316 * 317 * @param invoker 318 * The component responsible for showing the popup. can be null. 319 * Used such that when invoker pressed, the popup will not auto hide. 320 * 321 * @param loc 322 * Must not be null. 323 * 324 * @throws NullPointerException 325 * If p or loc is null 326 * 327 */ 328 public void showPopup(Popup p, Point loc, Component invoker) { 329 if (p == null) throw new NullPointerException("p"); 330 if (loc == null) throw new NullPointerException("animator"); 331 332 333 if (_popups.containsKey(p)) return; 334 335 p.prepareToPaint(); 336 p.setLocation(loc); 337 p.setVisible(true); 338 339 Browser._theBrowser.getLayeredPane().add(p, JLayeredPane.POPUP_LAYER, 0); 340 341 _popups.put(p, invoker); 342 if (invoker != null) _invokers.add(invoker); 343 344 p.onShowing(); 345 p.onShow(); 346 347 // Invalidate the popup border 348 if (p.getBorderThickness() > 0.0f) { 349 p.invalidateAppearance(); 350 } 351 } 352 353 /** 354 * Displays a popup at a specific location - with animation. 355 * 356 * @param p 357 * Must not be null. 358 * 359 * @param invoker 360 * The component responsible for showing the popup. can be null. 361 * Used such that when invoker pressed, the popup will not auto hide. 362 * 363 * @param loc 364 * Must not be null. 365 * 366 * @param animator 367 * Must not be null. 368 * 369 * @throws NullPointerException 370 * If p, animator or loc is null 371 */ 372 public void showPopup(Popup p, Point loc, Component invoker, PopupAnimator animator) { 373 if (animator == null) 374 throw new NullPointerException("animator"); 375 if (p == null) 376 throw new NullPointerException("p"); 377 if (loc == null) 378 throw new NullPointerException("loc"); 379 380 if (_popups.containsKey(p)) return; 381 382 _popups.put(p, invoker); 383 if (invoker != null) _invokers.add(invoker); 384 385 386 AnimatedPopup ap = new AnimatedPopup( 387 animator, 388 System.currentTimeMillis(), 389 p, 390 true, 391 loc); 392 393 394 animator.starting(true, new Rectangle(loc.x, loc.y, p.getWidth(), p.getHeight())); 395 396 p.onShowing(); 397 398 synchronized (_animatingPopups) { 399 _animatingPopups.add(ap); 400 } 401 402 if (_animationThread == null || !_animationThread.isAlive() || _animationThread.willDie) { 403 _animationThread = new AnimationThread(); 404 _animationThread.start(); 405 } 406 407 } 408 409 /** 410 * Does a pure asynch animation with no popups involved. 411 * 412 * For example you may want to have an effect such that an item is expanding 413 * or moving into a new location on the screen. 414 * 415 * @param animator 416 * Must not be null. 417 * 418 * @param target 419 * Must not be null. 420 * 421 * @throws NullPointerException 422 * If animator or target is null 423 * 424 */ 425 public void doPureAnimation(PopupAnimator animator, Rectangle target) { 426 427 if (animator == null) 428 throw new NullPointerException("animator"); 429 if (target == null) 430 throw new NullPointerException("target"); 431 432 AnimatedPopup ap = new AnimatedPopup( 433 animator, 434 System.currentTimeMillis(), 435 null, 436 false, 437 target.getLocation()); 438 439 440 animator.starting(true, target); 441 442 synchronized (_animatingPopups) { 443 _animatingPopups.add(ap); 444 } 445 446 if (_animationThread == null || !_animationThread.isAlive() || _animationThread.willDie) { 447 _animationThread = new AnimationThread(); 448 _animationThread.start(); 449 } 450 451 } 452 453 454 /** 455 * Paints all popups in the browsers popup pane with the given graphics. 456 * 457 * @param g 458 * Where to paint to. 459 * 460 * @param clip 461 */ 462 void paintLayeredPane(Graphics g, Area clip) { 463 if (Browser._theBrowser == null) return; 464 465 Component[] compsOnPopup = Browser._theBrowser.getLayeredPane().getComponentsInLayer(JLayeredPane.POPUP_LAYER); 466 467 for (int i = 0; i < compsOnPopup.length; i++) { 468 Component c = compsOnPopup[i]; 469 470 Point p = c.getLocation(); 471 472 if (clip == null || clip.intersects(c.getBounds())) { 473 g.translate(p.x, p.y); 474 c.paint(g); 475 g.translate(-p.x, -p.y); 476 } 477 478 } 169 public void frameChanged() 170 { 479 171 } 480 172 … … 482 174 * Paints current popup animations to the expeditee browser content pane. 483 175 */ 484 void paintAnimations() { 485 486 if (Browser._theBrowser == null 487 || Browser._theBrowser.g == null) return; 488 489 Graphics g = Browser._theBrowser.g; 490 491 synchronized (_animatingPopups) { 492 493 for (AnimatedPopup ap : _animatingPopups) { 494 ap.animator.paint(g); 495 } 496 497 } 498 } 499 500 /** 501 * Proccesses animation on a dedicated thread. 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. 502 193 * 503 194 * @author Brook Novak 504 * 505 */ 506 private class AnimationThread extends Thread { 507 508 private boolean willDie = false; 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 } 509 317 510 318 @Override 511 public void run() { 512 513 514 LinkedList<AnimatedPopup> finishedAnimations; 515 516 while (true) { 517 518 // Perform animation logic 519 finishedAnimations = animate(); 520 521 // Check if finished all animations 522 if (finishedAnimations == null) return; // done 523 524 // Check for finalization of animation. That is, adding the popups to the layered pane 525 boolean needsFinalization = false; 526 for (AnimatedPopup ap : finishedAnimations) { 527 if (ap.isShowing) { 528 needsFinalization = true; 529 break; 530 } 531 532 // FInal invalidation 533 FrameGraphics.invalidateArea(ap.animator.getCurrentDrawingArea()); 534 535 } 536 537 if (needsFinalization) { 538 SwingUtilities.invokeLater(new AnimationFinalizor(finishedAnimations)); 539 // Will repaint when a popup becomes anchored... 540 } else { 541 FrameGraphics.requestRefresh(true); 542 } 543 544 // Limit animation rate 545 try { 546 sleep(ANIMATION_RATE); 547 } catch (InterruptedException e) { 548 e.printStackTrace(); 549 } 550 551 } 552 553 } 554 555 /** 556 * Performs animations 557 * @return 558 */ 559 private LinkedList<AnimatedPopup> animate() { 560 561 LinkedList<AnimatedPopup> finishedPopups = null; 562 563 synchronized (_animatingPopups) { 564 565 if (_animatingPopups.isEmpty()) { 566 willDie = true; 567 return null; 568 } 569 570 long currentTime = System.currentTimeMillis(); 571 572 finishedPopups = new LinkedList<AnimatedPopup>(); 573 574 for (AnimatedPopup ap : _animatingPopups) { 575 576 long duration = currentTime - ap.startTime; 577 578 if (duration >= ANIMATION_DURATION) { // check if complete 579 580 finishedPopups.add(ap); 581 582 } else { 583 584 float percent = ((float)duration / (float)ANIMATION_DURATION); 585 assert (percent >= 0.0f); 586 assert (percent < 1.0f); 587 588 Rectangle dirty = ap.animator.update(percent); 589 if (dirty != null) 590 FrameGraphics.invalidateArea(dirty); 591 592 } 593 594 } 595 596 _animatingPopups.removeAll(finishedPopups); 597 598 } 599 600 return finishedPopups; 601 602 } 603 604 /** 605 * Adds popups to layered pane 606 * @author Brook Novak 607 * 608 */ 609 private class AnimationFinalizor implements Runnable { 610 611 private LinkedList<AnimatedPopup> finished; 612 613 AnimationFinalizor(LinkedList<AnimatedPopup> finished) { 614 this.finished = finished; 615 } 616 617 public void run() { 618 619 for (AnimatedPopup ap : finished) { 620 621 if (ap.isShowing && _popups.containsKey(ap.popup)) { 622 623 ap.popup.prepareToPaint(); 624 ap.popup.setLocation(ap.popupLocation); 625 ap.popup.setVisible(true); 626 627 Browser._theBrowser.getLayeredPane().add(ap.popup, JLayeredPane.POPUP_LAYER, 0); 628 629 ap.popup.onShow(); 630 631 // Invalidate the popup border 632 if (ap.popup.getBorderThickness() > 0.0f) { 633 ap.popup.invalidateAppearance(); 634 } 635 } 636 637 } 638 639 } 640 } 641 } 642 643 private class AnimatedPopup { 644 645 PopupAnimator animator; 646 long startTime; 647 Popup popup = null; 648 boolean isShowing; 649 Point popupLocation; 650 651 public AnimatedPopup(PopupAnimator animator, long startTime, Popup popup, 652 boolean isShowing, Point popupLocation) { 653 654 assert(animator != null); 655 assert(popupLocation != null); 656 657 // Only have popup if showing 658 assert (!isShowing && popup == null || (isShowing && popup != null)); 659 660 661 this.animator = animator; 662 this.startTime = startTime; 663 this.popup = popup; 664 this.isShowing = isShowing; 665 this.popupLocation = popupLocation; 666 667 } 668 669 } 670 671 /** 672 * Provides animations for a popup when hiding or when showing. 673 * 674 * Note that {@link PopupAnimator#paint(Graphics)} and {@link PopupAnimator#update(float)} 675 * will always be invoked at seperate times - so do not have to worry about thread-saefty. 676 * 677 * These should only be used once... one per popup at a time. 678 * 679 * @author Brook Novak 680 * 681 */ 682 public interface PopupAnimator { 683 684 /** 685 * Invoked before showing. Any prepaations are done here. 686 * 687 * @param isShowing 688 * True if this animation will be for a popup that is showing. 689 * False if this animation will be for a popup that is hiding. 690 * 691 * @param popupBounds 692 * The location of the popup. I.E. where it is, or where it will be. 693 * 694 */ 695 void starting(boolean isShowing, Rectangle popupBounds); 696 697 /** 698 * 699 * Called on an animation thread. 700 * 701 * @param percent 702 * The percent complete of the animations. 703 * Rangles from 0 to 1. 704 * 705 * @return dirty area that needs painting for last update... Null for no invalidation 706 * 707 * 708 */ 709 Rectangle update(float percent); 710 711 /** 712 * Paints the animation - on the swing thread. 713 * Note that this is always on the content pane - not the expeditee frame buffer. 714 * 715 * @param g 716 * 717 */ 718 void paint(Graphics g); 719 720 721 /** 722 * 723 * @return 724 * The area which the animation is drawn. Used for final invalidation. Null for no final invaliation 725 */ 726 Rectangle getCurrentDrawingArea(); 727 728 } 729 730 731 public class ExpandShrinkAnimator implements PopupAnimator { 732 733 private boolean isShowing; 734 private Rectangle popupBounds; 735 private Rectangle sourceRectangle; 736 private Rectangle currentRectangle; 737 private Color fillColor; 738 739 private final Stroke stroke = new BasicStroke(2.0f); 740 741 /** 742 * 743 * @param sourceRectangle 744 * @param fillColor 745 * The fill color of the animated rectangle. Null for no fill. 746 */ 747 public ExpandShrinkAnimator(Rectangle sourceRectangle, Color fillColor) { 748 if (sourceRectangle == null) throw new NullPointerException("sourceRectangle"); 749 750 this.fillColor = fillColor; 751 this.sourceRectangle = (Rectangle)sourceRectangle.clone(); 752 this.currentRectangle = (Rectangle)sourceRectangle.clone(); 753 } 754 755 public void paint(Graphics g) { 756 757 if (fillColor != null) { 758 g.setColor(fillColor); 759 g.fillRect(currentRectangle.x, currentRectangle.y, currentRectangle.width, currentRectangle.height); 760 } 761 762 g.setColor(Color.BLACK); 763 ((Graphics2D)g).setStroke(stroke); 764 g.drawRect(currentRectangle.x, currentRectangle.y, currentRectangle.width, currentRectangle.height); 765 } 766 767 public void starting(boolean isShowing, Rectangle popupBounds) { 768 this.isShowing = isShowing; 769 this.popupBounds = popupBounds; 770 771 if (isShowing) { 772 this.currentRectangle = (Rectangle)sourceRectangle.clone(); 773 } else { 774 this.currentRectangle = (Rectangle)sourceRectangle.clone(); 775 } 776 777 } 778 779 public Rectangle update(float percent) { 780 781 Rectangle oldBounds = currentRectangle; 782 783 if (!isShowing) { // if minimizing just reverse percent 784 percent = 1 - percent; 785 } 786 787 // update X 788 currentRectangle.x = sourceRectangle.x + 789 (int)((popupBounds.x - sourceRectangle.x) * percent); 790 791 // update Y 792 currentRectangle.y = sourceRectangle.y + 793 (int)((popupBounds.y - sourceRectangle.y) * percent); 794 795 // update width 796 currentRectangle.width = sourceRectangle.width + 797 (int)((popupBounds.width - sourceRectangle.width) * percent); 798 799 // update height 800 currentRectangle.height = sourceRectangle.height + 801 (int)((popupBounds.height - sourceRectangle.height) * percent); 802 803 int x = Math.min(oldBounds.x, currentRectangle.x); 804 int y = Math.min(oldBounds.y, currentRectangle.y); 805 int width = Math.min(oldBounds.x + oldBounds.width, currentRectangle.x + currentRectangle.width) - x; 806 int height = Math.min(oldBounds.y + oldBounds.height, currentRectangle.y + currentRectangle.height) - y; 807 808 return new Rectangle(x, y, width, height); 809 810 } 811 812 public Rectangle getCurrentDrawingArea() { 813 return currentRectangle; 814 } 815 816 817 } 818 819 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 } 820 336 }
Note:
See TracChangeset
for help on using the changeset viewer.