1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2005-2007 Adobe Systems Incorporated 5// All Rights Reserved. 6// 7// NOTICE: Adobe permits you to use, modify, and distribute this file 8// in accordance with the terms of the license agreement accompanying it. 9// 10//////////////////////////////////////////////////////////////////////////////// 11 12package mx.effects.effectClasses 13{ 14 15import flash.events.Event; 16import mx.containers.Panel; 17import mx.core.Application; 18import mx.core.Container; 19import mx.core.IUIComponent; 20import mx.core.ScrollPolicy; 21import mx.core.mx_internal; 22import mx.effects.EffectManager; 23import mx.events.EffectEvent; 24import mx.events.ResizeEvent; 25import mx.styles.IStyleClient; 26 27use namespace mx_internal; 28 29/** 30 * The ResizeInstance class implements the instance class 31 * for the Resize effect. 32 * Flex creates an instance of this class when it plays a Resize effect; 33 * you do not create one yourself. 34 * 35 * <p>Every effect class that is a subclass of the TweenEffect class 36 * supports the following events:</p> 37 * 38 * <ul> 39 * <li><code>tweenEnd</code>: Dispatched when the tween effect ends. </li> 40 * 41 * <li><code>tweenUpdate</code>: Dispatched every time a TweenEffect 42 * class calculates a new value.</li> 43 * </ul> 44 * 45 * <p>The event object passed to the event listener for these events is of type TweenEvent. 46 * The TweenEvent class defines the property <code>value</code>, which contains 47 * the tween value calculated by the effect. 48 * For the Resize effect, 49 * the <code>TweenEvent.value</code> property contains a 2-item Array, where: </p> 50 * <ul> 51 * <li>value[0]:Number A value between the values of the <code>Resize.widthFrom</code> 52 * and <code>Resize.widthTo</code> property.</li> 53 * 54 * <li>value[1]:Number A value between the values of the <code>Resize.heightFrom</code> 55 * and <code>Resize.heightTo</code> property.</li> 56 * </ul> 57 * 58 * @see mx.effects.Resize 59 * @see mx.events.TweenEvent 60 */ 61public class ResizeInstance extends TweenEffectInstance 62{ 63 include "../../core/Version.as"; 64 65 //-------------------------------------------------------------------------- 66 // 67 // Constructor 68 // 69 //-------------------------------------------------------------------------- 70 71 /** 72 * Constructor. 73 * 74 * @param target The Object to animate with this effect. 75 */ 76 public function ResizeInstance(target:Object) 77 { 78 super(target); 79 80 needToLayout = true; 81 } 82 83 //-------------------------------------------------------------------------- 84 // 85 // Variables 86 // 87 //-------------------------------------------------------------------------- 88 89 /** 90 * @private 91 */ 92 private var restoreVisibleArray:Array; 93 94 /** 95 * @private 96 */ 97 private var restoreAutoLayoutArray:Array; 98 99 /** 100 * @private 101 */ 102 private var numHideEffectsPlaying:Number = 0; 103 104 /** 105 * @private 106 */ 107 private var origPercentHeight:Number; 108 109 /** 110 * @private 111 */ 112 private var origPercentWidth:Number; 113 114 /** 115 * @private 116 */ 117 private var origExplicitHeight:Number; 118 119 /** 120 * @private 121 */ 122 private var origExplicitWidth:Number; 123 124 /** 125 * @private 126 */ 127 private var heightSet:Boolean; 128 129 /** 130 * @private 131 */ 132 private var widthSet:Boolean; 133 134 /** 135 * @private 136 */ 137 private var explicitWidthSet:Boolean; 138 139 /** 140 * @private 141 */ 142 private var explicitHeightSet:Boolean; 143 144 /** 145 * @private 146 */ 147 private var origVerticalScrollPolicy:String = ""; 148 149 /** 150 * @private 151 */ 152 private var origHorizontalScrollPolicy:String = ""; 153 154 /** 155 * @private 156 */ 157 private var parentOrigVerticalScrollPolicy:String = ""; 158 159 /** 160 * @private 161 */ 162 private var parentOrigHorizontalScrollPolicy:String = ""; 163 164 /** 165 * @private 166 * Stores the left style of the target 167 */ 168 private var left:*; 169 170 /** 171 * @private 172 * Stores the right style of the target 173 */ 174 private var right:*; 175 176 /** 177 * @private 178 * Stores the top style of the target 179 */ 180 private var top:*; 181 182 /** 183 * @private 184 * Stores the bottom style of the target 185 */ 186 private var bottom:*; 187 188 //-------------------------------------------------------------------------- 189 // 190 // Properties 191 // 192 //-------------------------------------------------------------------------- 193 194 //---------------------------------- 195 // heightBy 196 //---------------------------------- 197 198 /** 199 * @private 200 * Storage for the heightBy property. 201 */ 202 private var _heightBy:Number; 203 204 /** 205 * Number of pixels by which to modify the height of the component. 206 * Values may be negative. 207 */ 208 public function get heightBy():Number 209 { 210 return _heightBy; 211 } 212 213 /** 214 * @private 215 */ 216 public function set heightBy(value:Number):void 217 { 218 _heightBy = value; 219 heightSet = !isNaN(value); 220 } 221 222 //---------------------------------- 223 // heightFrom 224 //---------------------------------- 225 226 /** 227 * Initial height. If omitted, Flex uses the current size. 228 */ 229 public var heightFrom:Number; 230 231 //---------------------------------- 232 // heightTo 233 //---------------------------------- 234 235 /** 236 * @private 237 * Storage for the heightTo property. 238 */ 239 private var _heightTo:Number; 240 241 /** 242 * Final height, in pixels. 243 */ 244 public function get heightTo():Number 245 { 246 return _heightTo; 247 } 248 249 /** 250 * @private 251 */ 252 public function set heightTo(value:Number):void 253 { 254 _heightTo = value; 255 heightSet = !isNaN(value); 256 } 257 258 //---------------------------------- 259 // hideChildrenTargets 260 //---------------------------------- 261 262 /** 263 * An Array of Panels. 264 * The children of these Panels are hidden while the Resize effect plays. 265 */ 266 public var hideChildrenTargets:Array /* of Panel */; 267 268 //---------------------------------- 269 // widthBy 270 //---------------------------------- 271 272 /** 273 * @private 274 * Storage for the widthBy property. 275 */ 276 private var _widthBy:Number; 277 278 /** 279 * Number of pixels by which to modify the width of the component. 280 * Values may be negative. 281 */ 282 public function get widthBy():Number 283 { 284 return _widthBy; 285 } 286 287 /** 288 * @private 289 */ 290 public function set widthBy(value:Number):void 291 { 292 _widthBy = value; 293 widthSet = !isNaN(value); 294 } 295 296 //---------------------------------- 297 // widthFrom 298 //---------------------------------- 299 300 /** 301 * Initial width. If omitted, Flex uses the current size. 302 */ 303 public var widthFrom:Number; 304 305 //---------------------------------- 306 // widthTo 307 //---------------------------------- 308 309 /** 310 * @private 311 * Storage for the widthTo property. 312 */ 313 private var _widthTo:Number; 314 315 /** 316 * Final width, in pixels. 317 */ 318 public function get widthTo():Number 319 { 320 return _widthTo; 321 } 322 323 /** 324 * @private 325 */ 326 public function set widthTo(value:Number):void 327 { 328 _widthTo = value; 329 widthSet = !isNaN(value); 330 } 331 332 //-------------------------------------------------------------------------- 333 // 334 // Overridden methods 335 // 336 //-------------------------------------------------------------------------- 337 338 /** 339 * @private 340 */ 341 override public function initEffect(event:Event):void 342 { 343 super.initEffect(event); 344 345 if (event is ResizeEvent && event.type == ResizeEvent.RESIZE) 346 { 347 if (isNaN(widthBy)) 348 { 349 if (isNaN(widthFrom)) 350 { 351 widthFrom = ResizeEvent(event).oldWidth; 352 } 353 if (isNaN(widthTo)) 354 { 355 _widthTo = target.width; 356 } 357 } 358 359 if (isNaN(heightBy)) 360 { 361 if (isNaN(heightFrom)) 362 { 363 heightFrom = ResizeEvent(event).oldHeight; 364 } 365 366 if (isNaN(heightTo)) 367 { 368 _heightTo = target.height; 369 } 370 } 371 } 372 } 373 374 /** 375 * @private 376 */ 377 override public function play():void 378 { 379 // Dispatch an effectStart event from the target. 380 super.play(); 381 382 calculateExplicitDimensionChanges(); 383 384 // If the target is a Panel, then find all Panel objects that will 385 // be affected by the animation. Deliver a "resizeStart" event to 386 // each affected Panel, and then wait until the Panel finishes 387 // hiding its children. 388 var childrenHiding:Boolean = hidePanelChildren(); 389 390 if (target is IStyleClient) 391 { 392 left = target.getStyle("left"); 393 if (left != undefined) 394 target.setStyle("left",undefined); 395 396 right = target.getStyle("right"); 397 if (right != undefined) 398 target.setStyle("right",undefined); 399 400 top = target.getStyle("top"); 401 if (top != undefined) 402 target.setStyle("top",undefined); 403 404 bottom = target.getStyle("bottom"); 405 if (bottom != undefined) 406 target.setStyle("bottom",undefined); 407 } 408 409 if (!childrenHiding) 410 startResizeTween(); 411 } 412 413 /** 414 * @private 415 */ 416 override public function onTweenUpdate(value:Object):void 417 { 418 EffectManager.suspendEventHandling(); 419 420 // Use Math.round instead of Math.floor, so that the animation appears 421 // to begin playing after the value has changed by only half a pixel, 422 // instead of waiting for it to change by a whole pixel. 423 // Because of the way that the easing function mimics acceleration, 424 // it can take a while for the animation to get started. 425 target.width = Math.round(value[0]); 426 target.height = Math.round(value[1]); 427 428 // Set a flag indicating that LayoutManager.validateNow() 429 // should be called after we're finished processing 430 // all the effects for this frame. 431 if (tween) 432 tween.needToLayout = true; 433 needToLayout = true; 434 435 EffectManager.resumeEventHandling(); 436 } 437 438 /** 439 * @private 440 */ 441 override public function onTweenEnd(value:Object):void 442 { 443 EffectManager.endVectorEffect(IUIComponent(target)); 444 445 // Wait a frame before starting to restore the childrens' visibility. 446 // That way, we have a chance to run a measurement/layout pass with 447 // the final sizes and update the screen. 448 Application.application.callLater(restorePanelChildren); 449 450 super.onTweenEnd(value); 451 452 EffectManager.suspendEventHandling(); 453 454 var targetAsContainer:Container; 455 var parentAsContainer:Container; 456 457 // Restore the target's percent and explicit sizes 458 if (!heightSet) 459 { 460 target.percentHeight = origPercentHeight; 461 target.explicitHeight = origExplicitHeight; 462 463 if (origVerticalScrollPolicy != "") 464 { 465 targetAsContainer = target as Container; 466 if (targetAsContainer) 467 { 468 targetAsContainer.verticalScrollPolicy = origVerticalScrollPolicy; 469 origVerticalScrollPolicy = ""; 470 } 471 } 472 473 if (parentOrigVerticalScrollPolicy != "" && target.parent) 474 { 475 parentAsContainer = target.parent as Container; 476 if (parentAsContainer) 477 { 478 parentAsContainer.verticalScrollPolicy = parentOrigVerticalScrollPolicy; 479 parentOrigVerticalScrollPolicy = ""; 480 } 481 } 482 } 483 484 if (!widthSet) 485 { 486 target.percentWidth = origPercentWidth; 487 target.explicitWidth = origExplicitWidth; 488 489 if (origHorizontalScrollPolicy != "") 490 { 491 targetAsContainer = target as Container; 492 if (targetAsContainer) 493 { 494 targetAsContainer.horizontalScrollPolicy = origHorizontalScrollPolicy; 495 origHorizontalScrollPolicy = ""; 496 } 497 } 498 499 if (parentOrigHorizontalScrollPolicy != "" && target.parent) 500 { 501 parentAsContainer = target.parent as Container; 502 if (parentAsContainer) 503 { 504 parentAsContainer.horizontalScrollPolicy = parentOrigHorizontalScrollPolicy; 505 parentOrigHorizontalScrollPolicy = ""; 506 } 507 } 508 } 509 510 if (left != undefined) 511 target.setStyle("left",left); 512 if (right != undefined) 513 target.setStyle("right",right); 514 if (top != undefined) 515 target.setStyle("top",top); 516 if (bottom != undefined) 517 target.setStyle("bottom",bottom); 518 519 EffectManager.resumeEventHandling(); 520 } 521 522 /** 523 * @private 524 */ 525 override public function end():void 526 { 527 // If we were waiting for the initial "hide children" effect to 528 // finish playing, then the tween might not be created yet. 529 // In that case, we need to explicitly jump to the end of the Resize, 530 // because the TweenEffect.end() function won't do it for us. 531 532 if (!tween) 533 { 534 calculateExplicitDimensionChanges(); 535 536 onTweenEnd(playReversed ? 537 [ widthFrom, heightFrom ] : 538 [ widthTo, heightTo ]); 539 } 540 541 542 super.end(); 543 } 544 545 //-------------------------------------------------------------------------- 546 // 547 // Methods 548 // 549 //-------------------------------------------------------------------------- 550 551 /** 552 * @private 553 */ 554 private function startResizeTween():void 555 { 556 EffectManager.startVectorEffect(IUIComponent(target)); 557 558 // Create a tween to resize the object 559 tween = createTween(this, [ widthFrom, heightFrom ], 560 [ widthTo, heightTo ], duration); 561 562 // Set back to initial size before the screen refreshes. 563 //EffectManager.suspendEventHandling(); 564 applyTweenStartValues(); 565 /*if (needToLayout) 566 UIComponent.layoutManager.validateNow(); 567 */ 568 //EffectManager.resumeEventHandling(); 569 } 570 571 572 /** 573 * @private 574 * Hides children of Panels while the effect is playing. 575 */ 576 private function hidePanelChildren():Boolean 577 { 578 if (!hideChildrenTargets) 579 return false; 580 581 // Initialize a couple arrays that will be needed later 582 restoreVisibleArray = []; 583 restoreAutoLayoutArray = []; 584 585 // Send each panel a "resizeStart" event, which will trigger 586 // the resizeStartEffect (if any) 587 var n:int = hideChildrenTargets.length; 588 for (var i:int = 0; i < n; i++) 589 { 590 var p:Object = hideChildrenTargets[i]; 591 592 if (p is Panel) 593 { 594 var prevNumHideEffectsPlaying:Number = numHideEffectsPlaying; 595 596 p.addEventListener(EffectEvent.EFFECT_START, eventHandler); 597 p.dispatchEvent(new Event("resizeStart")); 598 p.removeEventListener(EffectEvent.EFFECT_START, eventHandler); 599 600 // If no effect started playing, then make children invisible 601 // immediately instead of waiting for the end of the effect 602 if (numHideEffectsPlaying == prevNumHideEffectsPlaying) 603 makePanelChildrenInvisible(Panel(p), i); 604 } 605 } 606 607 return numHideEffectsPlaying > 0; 608 } 609 610 /** 611 * @private 612 */ 613 private function makePanelChildrenInvisible(panel:Container, 614 panelIndex:Number):void 615 { 616 var childArray:Array = []; 617 618 var child:IUIComponent; 619 620 // Hide the Panel's children while the Resize is occurring. 621 var n:int = panel.numChildren; 622 for (var i:int = 0; i < n; i++) 623 { 624 child = IUIComponent(panel.getChildAt(i)); 625 if (child.visible) 626 { 627 childArray.push(child); 628 child.setVisible(false, true); 629 } 630 } 631 632 // Hide the Panel's scrollbars while the Resize is occurring. 633 child = panel.horizontalScrollBar; 634 if (child && child.visible) 635 { 636 childArray.push(child); 637 child.setVisible(false, true); 638 } 639 child = panel.verticalScrollBar; 640 if (child && child.visible) 641 { 642 childArray.push(child); 643 child.setVisible(false, true); 644 } 645 646 restoreVisibleArray[panelIndex] = childArray; 647 648 // Set autoLayout = false, which prevents the Panel's updateDisplayList() 649 // method from executing while the Panel is resizing. 650 if (panel.autoLayout) 651 { 652 panel.autoLayout = false; 653 restoreAutoLayoutArray[panelIndex] = true; 654 } 655 } 656 657 /** 658 * Method is used to explicitely determine widthTo and heightTo, taking into 659 * account the current state of the component and the inputs to this ResizeEffect 660 * 661 * @private 662 */ 663 private function calculateExplicitDimensionChanges():void 664 { 665 var explicitWidth:* = propertyChanges ? propertyChanges.end["explicitWidth"] : undefined; 666 var explicitHeight:* = propertyChanges ? propertyChanges.end["explicitHeight"] : undefined; 667 var percentWidth:* = propertyChanges ? propertyChanges.end["percentWidth"] : undefined; 668 var percentHeight:* = propertyChanges ? propertyChanges.end["percentHeight"] : undefined; 669 670 var targetAsContainer:Container; 671 var parentAsContainer:Container; 672 673 if (!heightSet) 674 { 675 // Determine the percentHeight/explicitHeight to apply to target when effect ends 676 if (percentHeight !== undefined) 677 origPercentHeight = percentHeight; 678 else 679 origPercentHeight = target.percentHeight; 680 681 if (isNaN(origPercentHeight)) 682 { 683 if (explicitHeight !== undefined) 684 origExplicitHeight = explicitHeight; 685 else 686 origExplicitHeight = target.explicitHeight; 687 } 688 689 targetAsContainer = target as Container; 690 if (targetAsContainer && targetAsContainer.verticalScrollBar == null) 691 { 692 origVerticalScrollPolicy = targetAsContainer.verticalScrollPolicy; 693 targetAsContainer.verticalScrollPolicy = ScrollPolicy.OFF; 694 } 695 696 if (target.parent) 697 { 698 parentAsContainer = target.parent as Container; 699 if (parentAsContainer && parentAsContainer.verticalScrollBar == null) 700 { 701 parentOrigVerticalScrollPolicy = parentAsContainer.verticalScrollPolicy; 702 parentAsContainer.verticalScrollPolicy = ScrollPolicy.OFF; 703 } 704 } 705 } 706 707 if (!widthSet) 708 { 709 // Determine the percentHeight/explicitHeight to apply to target when effect ends 710 if (percentWidth !== undefined) 711 origPercentWidth = percentWidth; 712 else 713 origPercentWidth = target.percentWidth; 714 715 if (isNaN(origPercentWidth)) 716 { 717 if (explicitWidth !== undefined) 718 origExplicitWidth = explicitWidth; 719 else 720 origExplicitWidth = target.explicitWidth; 721 } 722 723 targetAsContainer = target as Container; 724 if (targetAsContainer && targetAsContainer.horizontalScrollBar == null) 725 { 726 origHorizontalScrollPolicy = targetAsContainer.horizontalScrollPolicy; 727 targetAsContainer.horizontalScrollPolicy = ScrollPolicy.OFF; 728 } 729 730 if (target.parent) 731 { 732 parentAsContainer = target.parent as Container; 733 if (parentAsContainer && parentAsContainer.horizontalScrollBar == null) 734 { 735 parentOrigHorizontalScrollPolicy = parentAsContainer.horizontalScrollPolicy; 736 parentAsContainer.horizontalScrollPolicy = ScrollPolicy.OFF; 737 } 738 } 739 } 740 741 // The user may have supplied some combination of widthFrom, 742 // widthTo, and widthBy. If either widthFrom or widthTo is 743 // not explicitly defined, calculate its value based on the 744 // other two values. 745 if (isNaN(widthFrom)) 746 { 747 widthFrom = (!isNaN(widthTo) && !isNaN(widthBy)) ? 748 widthTo - widthBy : 749 target.width; 750 } 751 if (isNaN(widthTo)) 752 { 753 if (isNaN(widthBy) && 754 propertyChanges && 755 (propertyChanges.end["width"] !== undefined || 756 explicitWidth !== undefined )) 757 { 758 if (explicitWidth !== undefined && !isNaN(explicitWidth)) 759 { 760 explicitWidthSet = true; 761 _widthTo = explicitWidth; 762 } 763 else 764 { 765 _widthTo = propertyChanges.end["width"]; 766 } 767 } 768 else 769 { 770 _widthTo = (!isNaN(widthBy)) ? 771 widthFrom + widthBy : 772 target.width; 773 } 774 } 775 776 // Ditto for heightFrom, heightTo, and heightBy. 777 if (isNaN(heightFrom)) 778 { 779 heightFrom = (!isNaN(heightTo) && !isNaN(heightBy)) ? 780 heightTo - heightBy : 781 target.height; 782 } 783 if (isNaN(heightTo)) 784 { 785 if (isNaN(heightBy) && 786 propertyChanges && 787 (propertyChanges.end["height"] !== undefined || 788 explicitHeight !== undefined)) 789 { 790 if (explicitHeight !== undefined && !isNaN(explicitHeight)) 791 { 792 explicitHeightSet = true; 793 _heightTo = explicitHeight; 794 } 795 else 796 { 797 _heightTo = propertyChanges.end["height"]; 798 } 799 } 800 else 801 { 802 _heightTo = (!isNaN(heightBy))? 803 heightFrom + heightBy : 804 target.height; 805 } 806 } 807 } 808 809 /** 810 * @private 811 */ 812 private function restorePanelChildren():void 813 { 814 if (hideChildrenTargets) 815 { 816 var n:int = hideChildrenTargets.length; 817 for (var i:int = 0; i < n; i++) 818 { 819 var p:IUIComponent = hideChildrenTargets[i]; 820 821 var childArray:Array = restoreVisibleArray[i]; 822 if (childArray) 823 { 824 var m:int = childArray.length; 825 for (var j:int = 0; j < m; j++) 826 { 827 childArray[j].setVisible(true, true); 828 } 829 } 830 831 if (restoreAutoLayoutArray[i]) 832 Container(p).autoLayout = true; 833 834 // Trigger the resizeEndEffect (if any) 835 p.dispatchEvent(new Event("resizeEnd")); 836 } 837 } 838 } 839 840 //-------------------------------------------------------------------------- 841 // 842 // Overridden event handlers 843 // 844 //-------------------------------------------------------------------------- 845 846 /** 847 * @private 848 * This function is called when one of the Panels finishes 849 * its "hide children" animation. 850 */ 851 override mx_internal function eventHandler(event:Event):void 852 { 853 var panel:Container = event.target as Container; 854 855 super.eventHandler(event); 856 857 if (event.type == EffectEvent.EFFECT_START) 858 { 859 // Call my eventHandler() method when the effect finishes playing. 860 panel.addEventListener(EffectEvent.EFFECT_END, eventHandler); 861 862 // Remember how many effects we're waiting for 863 numHideEffectsPlaying++; 864 } 865 866 else if (event.type == EffectEvent.EFFECT_END) 867 { 868 // Remove the event listener that triggered this callback. 869 panel.removeEventListener(EffectEvent.EFFECT_END, eventHandler); 870 871 // Get the array index of the panel 872 var n:int = hideChildrenTargets.length; 873 for (var i:int = 0; i < n; i++) 874 { 875 if (hideChildrenTargets[i] == panel) 876 break; 877 } 878 879 makePanelChildrenInvisible(panel, i); 880 881 // If all panels have finished their "hide children" effect, 882 // then it's time to start our Resize effect. 883 if (--numHideEffectsPlaying == 0) 884 startResizeTween(); 885 } 886 } 887} 888 889} 890