//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.managers { import flash.display.DisplayObject; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.Timer; import mx.controls.ToolTip; import mx.core.FlexGlobals; import mx.core.FlexVersion; import mx.core.IFlexModule; import mx.core.IInvalidating; import mx.core.ILayoutDirectionElement; import mx.core.IToolTip; import mx.core.IUIComponent; import mx.core.LayoutDirection; import mx.core.mx_internal; import mx.effects.EffectManager; import mx.effects.IAbstractEffect; import mx.events.DynamicEvent; import mx.events.EffectEvent; import mx.events.ToolTipEvent; import mx.managers.IToolTipManagerClient; import mx.styles.IStyleClient; import mx.validators.IValidatorListener; use namespace mx_internal; [ExcludeClass] /** * @private * The ToolTipManager lets you set basic ToolTip and error tip functionality, * such as display delay and the disabling of ToolTips. * * @see mx.controls.ToolTip * @see mx.validators.Validator */ public class ToolTipManagerImpl extends EventDispatcher implements IToolTipManager2 { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class variables // //-------------------------------------------------------------------------- /** * @private */ private static var instance:IToolTipManager2; /** * @private * * Place to hook in additional classes */ public static var mixins:Array; //-------------------------------------------------------------------------- // // Class methods // //-------------------------------------------------------------------------- /** * @private */ public static function getInstance():IToolTipManager2 { if (!instance) instance = new ToolTipManagerImpl(); return instance; } //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * @private */ public function ToolTipManagerImpl() { super(); if (instance) throw new Error("Instance already exists."); if (mixins) { var n:int = mixins.length; for (var i:int = 0; i < n; i++) { new mixins[i](this); } } if (hasEventListener("initialize")) dispatchEvent(new Event("initialize")); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * A flag that keeps track of whether this class's initialize() * method has been executed. */ mx_internal var initialized:Boolean = false; /** * @private * This timer is used to delay the appearance of a normal ToolTip * after the mouse moves over a target; an error tip has no such delay. * *

This timer, which is lazily created, is started when the mouse * moves over an object with a ToolTip, with a duration specified * by showDelay. * If the mouse moves out of this object before the timer fires, * the ToolTip is never created. * If the mouse stays over the object until the timer fires, * the ToolTip is created and its showEffect is started. */ mx_internal var showTimer:Timer; /** * @private * This timer is used to make the tooltip "time out" and hide itself * if the mouse stays over a target. * *

This timer, which is lazily created, is started * when the showEffect ends. * When it fires, the hideEffect is started.

*/ mx_internal var hideTimer:Timer; /** * @private * This timer is used to implement mousing quickly over multiple targets * with ToolTip... * *

This timer, which is lazily created, is started * when ...

*/ mx_internal var scrubTimer:Timer; /** * @private */ mx_internal var currentText:String; /** * @private */ mx_internal var isError:Boolean; /** * The UIComponent with the ToolTip assigned to it * that was most recently under the mouse. * During much of the tool tip life cycle this property * has the same value as the currentTarget property. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal var previousTarget:DisplayObject; //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // currentTarget //---------------------------------- /** * @private */ private var _currentTarget:DisplayObject; /** * The UIComponent that is currently displaying a ToolTip, * or null if none is. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get currentTarget():DisplayObject { return _currentTarget; } /** * @private */ public function set currentTarget(value:DisplayObject):void { _currentTarget = value; } //---------------------------------- // currentToolTip //---------------------------------- /** * @private */ mx_internal var _currentToolTip:DisplayObject; /** * The ToolTip object that is currently visible, * or null if none is shown. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get currentToolTip():IToolTip { return _currentToolTip as IToolTip; } /** * @private */ public function set currentToolTip(value:IToolTip):void { _currentToolTip = value as DisplayObject; if (hasEventListener("currentToolTip")) dispatchEvent(new Event("currentToolTip")); } //---------------------------------- // enabled //---------------------------------- /** * @private */ private var _enabled:Boolean = true; /** * If true, the ToolTipManager will automatically show * ToolTips when the user moves the mouse pointer over components. * If false, no ToolTips will be shown. * * @default true * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get enabled():Boolean { return _enabled; } /** * @private */ public function set enabled(value:Boolean):void { _enabled = value; } //---------------------------------- // hideDelay //---------------------------------- /** * @private */ private var _hideDelay:Number = 10000; // milliseconds /** * The amount of time, in milliseconds, that Flex waits * to hide the ToolTip after it appears. * Once Flex hides a ToolTip, the user must move the mouse * off the component and then back onto it to see the ToolTip again. * If you set hideDelay to Infinity, * Flex does not hide the ToolTip until the user triggers an event, * such as moving the mouse off of the component. * * @default 10000 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get hideDelay():Number { return _hideDelay; } /** * @private */ public function set hideDelay(value:Number):void { _hideDelay = value; } //---------------------------------- // hideEffect //---------------------------------- /** * @private */ private var _hideEffect:IAbstractEffect; /** * The effect that plays when a ToolTip is hidden, * or null if the ToolTip should disappear with no effect. * *

Effects are not marshaled across applicationDomains in a sandbox * as they may not be supportable in different versions

* * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get hideEffect():IAbstractEffect { return _hideEffect; } /** * @private */ public function set hideEffect(value:IAbstractEffect):void { _hideEffect = value as IAbstractEffect; } //---------------------------------- // scrubDelay //---------------------------------- /** * @private */ private var _scrubDelay:Number = 100; // milliseconds /** * The amount of time, in milliseconds, that a user can take * when moving the mouse between controls before Flex again waits * for the duration of showDelay to display a ToolTip. * *

This setting is useful if the user moves quickly from one control * to another; after displaying the first ToolTip, Flex will display * the others immediately rather than waiting. * The shorter the setting for scrubDelay, the more * likely that the user must wait for an amount of time specified * by showDelay in order to see the next ToolTip. * A good use of this property is if you have several buttons on a * toolbar, and the user will quickly scan across them to see brief * descriptions of their functionality.

* * @default 100 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get scrubDelay():Number { return _scrubDelay; } /** * @private */ public function set scrubDelay(value:Number):void { _scrubDelay = value; } //---------------------------------- // showDelay //---------------------------------- /** * @private */ private var _showDelay:Number = 500; // milliseconds /** * The amount of time, in milliseconds, that Flex waits * before displaying the ToolTip box once a user * moves the mouse over a component that has a ToolTip. * To make the ToolTip appear instantly, set showDelay to 0. * * @default 500 * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get showDelay():Number { return _showDelay; } /** * @private */ public function set showDelay(value:Number):void { _showDelay = value; } //---------------------------------- // showEffect //---------------------------------- /** * @private */ private var _showEffect:IAbstractEffect; /** * The effect that plays when a ToolTip is shown, * or null if the ToolTip should appear with no effect. * *

Effects are not marshaled across applicationDomains in a sandbox * as they may not be supportable in different versions

* * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get showEffect():IAbstractEffect { return _showEffect; } /** * @private */ public function set showEffect(value:IAbstractEffect):void { _showEffect = value as IAbstractEffect; } //---------------------------------- // toolTipClass //---------------------------------- /** * @private */ private var _toolTipClass:Class = ToolTip; /** * The class to use for creating ToolTips. * *

The ToolTipClass is not marshaled across applicationDomains in a sandbox * as they may not be supportable in different versions. Child * applications should only be interested in setting the tooltip * for objects within themselves

* * @default mx.controls.ToolTip * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get toolTipClass():Class { return _toolTipClass; } /** * @private */ public function set toolTipClass(value:Class):void { _toolTipClass = value; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @private * Initializes the class. * *

This method sets up three Timer objects that ToolTipManager * starts and stops while tracking the mouse. * The repeatCount is set to 1 so that they fire only once. * Their duration is set later, just before they are started. * The timers are never destroyed once they are created here.

* *

This method is called by targetChanged(); Flex waits to initialize * the class until mouse-tracking happens in order to optimize * startup time.

*/ mx_internal function initialize():void { if (!showTimer) { showTimer = new Timer(0, 1); showTimer.addEventListener(TimerEvent.TIMER, showTimer_timerHandler); } if (!hideTimer) { hideTimer = new Timer(0, 1); hideTimer.addEventListener(TimerEvent.TIMER, hideTimer_timerHandler); } if (!scrubTimer) scrubTimer = new Timer(0, 1); initialized = true; } /** * Registers a target UIComponent or UITextField, and the text * for its ToolTip, with the ToolTipManager. * This causes the ToolTipManager to display a ToolTip * when the mouse hovers over the target. * *

This method is called by the setter * for the toolTip property in UIComponent and UITextField.

* * @param target The UIComponent or UITextField that owns the ToolTip. * * @param oldToolTip The old text that was displayed * in the ToolTip. * * @param newToolTip The new text to display in the ToolTip. * If null, no ToolTip will be displayed when the mouse hovers * over the target. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function registerToolTip(target:DisplayObject, oldToolTip:String, newToolTip:String):void { if (!oldToolTip && newToolTip) { target.addEventListener(MouseEvent.MOUSE_OVER, toolTipMouseOverHandler); target.addEventListener(MouseEvent.MOUSE_OUT, toolTipMouseOutHandler); // If the mouse is already over the object // that's getting a toolTip, show the tip. if (mouseIsOver(target)) showImmediately(target); } else if (oldToolTip && !newToolTip) { target.removeEventListener(MouseEvent.MOUSE_OVER, toolTipMouseOverHandler); target.removeEventListener(MouseEvent.MOUSE_OUT, toolTipMouseOutHandler); // If the mouse is over the object whose toolTip // is being removed, hide the tip. if (mouseIsOver(target)) hideImmediately(target); } } /** * Registers a target UIComponent, and the text * for its error tip, with the ToolTipManager. * This causes the ToolTipManager to display an error tip * when the mouse hovers over the target. * *

This method is called by the setter * for the errorString property in UIComponent.

* * @param target The UIComponent or UITextField that owns the ToolTip. * * @param oldErrorString The old text that was displayed * in the error tip. * * @param newErrorString The new text to display in the error tip. * If null, no error tip will be displayed when the mouse hovers * over the target. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function registerErrorString(target:DisplayObject, oldErrorString:String, newErrorString:String):void { if (!oldErrorString && newErrorString) { target.addEventListener(MouseEvent.MOUSE_OVER, errorTipMouseOverHandler); target.addEventListener(MouseEvent.MOUSE_OUT, errorTipMouseOutHandler); // If the mouse is already over the object // that's getting an errorTip, show the tip. if (mouseIsOver(target)) showImmediately(target); } else if (oldErrorString && !newErrorString) { target.removeEventListener(MouseEvent.MOUSE_OVER, errorTipMouseOverHandler); target.removeEventListener(MouseEvent.MOUSE_OUT, errorTipMouseOutHandler); // If the mouse is over the object whose toolTip // is being removed, hide the tip. if (mouseIsOver(target)) hideImmediately(target); } } /** * @private * Returns true if the mouse is over the specified target. */ private function mouseIsOver(target:DisplayObject):Boolean { if (!target || !target.stage) return false; //SDK:13465 - If we pass through the above if block, then //we have a target component and its been added to the //display list. If the mouse coordinates are (0,0), there //is a chance the component has not been positioned yet //and we'll end up mistakenly showing tooltips since the //target hitTest will return true. if ((target.stage.mouseX == 0) && (target.stage.mouseY == 0)) return false; if (target is ILayoutManagerClient && !ILayoutManagerClient(target).initialized) return false; return target.hitTestPoint(target.stage.mouseX, target.stage.mouseY, true); } /** * @private * Shows the tip immediately when the toolTip or errorTip property * becomes non-null and the mouse is over the target. */ private function showImmediately(target:DisplayObject):void { var oldShowDelay:Number = ToolTipManager.showDelay; ToolTipManager.showDelay = 0; checkIfTargetChanged(target); ToolTipManager.showDelay = oldShowDelay; } /** * @private * Hides the tip immediately when the toolTip or errorTip property * becomes null and the mouse is over the target. */ private function hideImmediately(target:DisplayObject):void { checkIfTargetChanged(null); } /** * Replaces the ToolTip, if necessary. * *

Determines whether the UIComponent or UITextField object * with the ToolTip assigned to it that is currently under the mouse * pointer is the most recent such object. * If not, it removes the old ToolTip and displays the new one.

* * @param displayObject The UIComponent or UITextField that is currently under the mouse. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function checkIfTargetChanged(displayObject:DisplayObject):void { if (!enabled) return; findTarget(displayObject); if (currentTarget != previousTarget) { targetChanged(); previousTarget = currentTarget; } } /** * Searches from the displayObject object up the chain * of parent objects until it finds a UIComponent or UITextField object * with a toolTip or errorString property. * Treats an empty string as a valid toolTip property. * Sets the currentTarget property. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function findTarget(displayObject:DisplayObject):void { // Walk up the DisplayObject parent chain looking for a UIComponent // with a toolTip or errorString property. Note that we stop // even if we find a tooltip which is an empty string. Although // we don't display empty tooltips, we have to track when we // are over a movieclip with an empty tooltip so that we can // hide any previous tooltip. This allows a child to set // toolTip="" to "cancel" its parent's toolTip. while (displayObject) { if (displayObject is IValidatorListener) { currentText = IValidatorListener(displayObject).errorString; var showErrorTip:Boolean; if (displayObject is IStyleClient) showErrorTip = FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0 || IStyleClient(displayObject).getStyle("showErrorTip"); ; if (currentText != null && currentText != "" && showErrorTip) { currentTarget = displayObject; isError = true; return; } } if (displayObject is IToolTipManagerClient) { currentText = IToolTipManagerClient(displayObject).toolTip; if (currentText != null) { currentTarget = displayObject; isError = false; return; } } displayObject = displayObject.parent; } currentText = null; currentTarget = null; } /** * Removes any ToolTip that is currently displayed and displays * the ToolTip for the UIComponent that is currently under the mouse * pointer, as determined by the currentTarget property. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function targetChanged():void { // Do lazy creation of the Timer objects this class uses. if (!initialized) initialize() var event:ToolTipEvent; if (previousTarget && currentToolTip) { if (currentToolTip is IToolTip) { event = new ToolTipEvent(ToolTipEvent.TOOL_TIP_HIDE); event.toolTip = currentToolTip; previousTarget.dispatchEvent(event); } else { if (hasEventListener(ToolTipEvent.TOOL_TIP_HIDE)) dispatchEvent(new Event(ToolTipEvent.TOOL_TIP_HIDE)); } } reset(); if (currentTarget) { // Don't display empty tooltips. if (currentText == "") return; // Dispatch a "startToolTip" event // from the object displaying the tooltip. event = new ToolTipEvent(ToolTipEvent.TOOL_TIP_START); currentTarget.dispatchEvent(event); if (showDelay == 0 || scrubTimer.running) { // Create the tooltip and start its showEffect. createTip(); initializeTip(); positionTip(); showTip(); } else { showTimer.delay = showDelay; showTimer.start(); // After the delay, showTimer_timerHandler() // will create the tooltip and start its showEffect. } } } /** * Creates an invisible new ToolTip. * *

If the ToolTipManager's enabled property is * true this method is automatically called * when the user moves the mouse over an object that has * the toolTip property set, * The ToolTipManager makes subsequent calls to * initializeTip(), positionTip(), * and showTip() to complete the display * of the ToolTip.

* *

The type of ToolTip that is created is determined by the * toolTipClass property. * By default, this is the ToolTip class. * This class can be styled to appear as either a normal ToolTip * (which has a yellow background by default) or as an error tip * for validation errors (which is red by default).

* *

After creating the ToolTip with the new * operator, this method stores a reference to it in the * currentToolTip property. * It then uses addChild() to add this ToolTip to the * SystemManager's toolTips layer.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function createTip():void { // Dispatch a "createToolTip" event // from the object displaying the tooltip. var event:ToolTipEvent = new ToolTipEvent(ToolTipEvent.TOOL_TIP_CREATE); currentTarget.dispatchEvent(event); if (event.toolTip) currentToolTip = event.toolTip; else currentToolTip = new toolTipClass(); currentToolTip.visible = false; // Set the tooltip to be in the same module factory as the target to the // correct style manager is used. Don't overwrite an existing module factory. if (currentToolTip is IFlexModule && IFlexModule(currentToolTip).moduleFactory == null && currentTarget is IFlexModule) IFlexModule(currentToolTip).moduleFactory = IFlexModule(currentTarget).moduleFactory; if (hasEventListener("createTip")) if (!dispatchEvent(new Event("createTip", false, true))) return; var sm:ISystemManager = getSystemManager(currentTarget) as ISystemManager; sm.topLevelSystemManager.toolTipChildren.addChild(currentToolTip as DisplayObject); } /** * Initializes a newly created ToolTip with the appropriate text, * based on the object under the mouse. * *

If the ToolTipManager's enabled property is * true this method is automatically called * when the user moves the mouse over an object that has * the toolTip property set. * The ToolTipManager calls createTip() before * this method, and positionTip() and * showTip() after.

* *

If a normal ToolTip is being displayed, this method * sets its text as specified by the toolTip * property of the object under the mouse. * If an error tip is being displayed, the text is as * specified by the errorString property * of the object under the mouse.

* *

This method also makes the ToolTip the appropriate * size for the text that it needs to display.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function initializeTip():void { // Set the text of the tooltip. if (currentToolTip is IToolTip) IToolTip(currentToolTip).text = currentText; if (isError && currentToolTip is IStyleClient) IStyleClient(currentToolTip).setStyle("styleName", "errorTip"); sizeTip(currentToolTip); if (currentToolTip is IStyleClient) { // Set up its "show" and "hide" effects. if (showEffect) IStyleClient(currentToolTip).setStyle("showEffect", showEffect); if (hideEffect) IStyleClient(currentToolTip).setStyle("hideEffect", hideEffect); } if (showEffect || hideEffect) { currentToolTip.addEventListener(EffectEvent.EFFECT_END, effectEndHandler); } } /** * @private * Objects added to the SystemManager's ToolTip layer don't get * automatically measured or sized, so ToolTipManager has to * measure it and set its size. */ public function sizeTip(toolTip:IToolTip):void { // Force measure() to be called on the tooltip. // Otherwise, its measured size will be 0. if (toolTip is IInvalidating) IInvalidating(toolTip).validateNow(); toolTip.setActualSize( toolTip.getExplicitOrMeasuredWidth(), toolTip.getExplicitOrMeasuredHeight()); } /** * Positions a newly created and initialized ToolTip on the stage. * *

If the ToolTipManager's enabled property is * true this method is automatically called * when the user moves the mouse over an object that has * the toolTip property set. * The ToolTipManager calls createTip() and * initializeTip() before this method, * and showTip() after.

* *

If a normal ToolTip is being displayed, this method positions * its upper-left (upper-right) corner near the lower-right (lower-left) * of the arrow cursor. This method ensures that the ToolTip is * completely in view. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function positionTip():void { // Determine layoutDirection of the target component. var layoutDirection:String; if (currentTarget is ILayoutDirectionElement) layoutDirection = ILayoutDirectionElement(currentTarget).layoutDirection; else layoutDirection = LayoutDirection.LTR; const mirror:Boolean = (layoutDirection == LayoutDirection.RTL); var x:Number; var y:Number; var screenWidth:Number = currentToolTip.screen.width; var screenHeight:Number = currentToolTip.screen.height; if (isError) { // Tooltips are laid out in the same direction as the target // component. var tipElt:ILayoutDirectionElement = currentToolTip as ILayoutDirectionElement; if (tipElt && tipElt.layoutDirection != layoutDirection) { tipElt.layoutDirection = layoutDirection; // sizeTip below will call validateNow() tipElt.invalidateLayoutDirection(); } var targetGlobalBounds:Rectangle = getGlobalBounds(currentTarget, currentToolTip.root, mirror); x = mirror ? targetGlobalBounds.left - 4 : targetGlobalBounds.right + 4; y = targetGlobalBounds.top - 1; // If there's no room to the right (left) of the control, put it // above or below, with the left (right) edge of the error tip // aligned with the left (right) edge of the target. var noRoom:Boolean = mirror ? x < currentToolTip.width : x + currentToolTip.width > screenWidth; if (noRoom) { var newWidth:Number = NaN; var oldWidth:Number = NaN; x = mirror ? targetGlobalBounds.right + 2 - currentToolTip.width : targetGlobalBounds.left - 2; // If the error tip would be too wide for the stage, // reduce the maximum width to fit onstage. Note that // we have to reassign the text in order to get the tip // to relayout after changing the border style and maxWidth. if (mirror) { if (x < currentToolTip.width + 4) { // -4 on the left, +2 on the right = -2 x = 4; newWidth = targetGlobalBounds.right - 2; } } else { if (x + currentToolTip.width + 4 > screenWidth) newWidth = screenWidth - x - 4; } if (!isNaN(newWidth)) { oldWidth = Object(toolTipClass).maxWidth; Object(toolTipClass).maxWidth = newWidth; if (currentToolTip is IStyleClient) IStyleClient(currentToolTip).setStyle("borderStyle", "errorTipAbove"); currentToolTip["text"] = currentToolTip["text"]; } // Even if the error tip will fit onstage, we still need to // change the border style and get the error tip to relayout. else { if (currentToolTip is IStyleClient) IStyleClient(currentToolTip).setStyle("borderStyle", "errorTipAbove"); currentToolTip["text"] = currentToolTip["text"]; } if (currentToolTip.height + 2 < targetGlobalBounds.top) { // There's room to put it above the control. y = targetGlobalBounds.top - (currentToolTip.height + 2); } else { // No room above, put it below the control. y = targetGlobalBounds.bottom + 2; if (!isNaN(newWidth)) Object(toolTipClass).maxWidth = newWidth; if (currentToolTip is IStyleClient) IStyleClient(currentToolTip).setStyle("borderStyle", "errorTipBelow"); currentToolTip["text"] = currentToolTip["text"]; } } // Since the border style of the error tip may have changed, // we have to force a remeasurement and change its size. // This is because objects in the toolTips layer // don't undergo normal measurement and layout. sizeTip(currentToolTip) // If we changed the tooltip max size, we change it back. // Otherwise, if RTL, and x wasn't set for maxWidth, reposition // because the width may have changed during the remeasure. if (!isNaN(oldWidth)) Object(toolTipClass).maxWidth = oldWidth; else if (mirror) x = targetGlobalBounds.right + 2 - currentToolTip.width; } else { var sm:ISystemManager = getSystemManager(currentTarget); // Position the upper-left (upper-right) of the tooltip // at the lower-right (lower-left) of the arrow cursor. x = DisplayObject(sm).mouseX + 11; if (mirror) x -= currentToolTip.width; y = DisplayObject(sm).mouseY + 22; // If the tooltip is too wide to fit onstage, move it left (right). var toolTipWidth:Number = currentToolTip.width; if (mirror) { if (x < 2) x = 2; } else if (x + toolTipWidth > screenWidth) { x = screenWidth - toolTipWidth; } // If the tooltip is too tall to fit onstage, move it up. var toolTipHeight:Number = currentToolTip.height; if (y + toolTipHeight > screenHeight) y = screenHeight - toolTipHeight; var pos:Point = new Point(x, y); pos = DisplayObject(sm).localToGlobal(pos); pos = DisplayObject(sm.getSandboxRoot()).globalToLocal(pos); x = pos.x; y = pos.y; } currentToolTip.move(x, y); } /** * Shows a newly created, initialized, and positioned ToolTip. * *

If the ToolTipManager's enabled property is * true this method is automatically called * when the user moves the mouse over an object that has * the toolTip property set. * The ToolTipManager calls createTip(), * initializeTip(), and positionTip() * before this method.

* *

This method first dispatches a "showToolTip" * event from the object under the mouse. * This gives you a chance to do special processing on a * particular object's ToolTip just before it becomes visible. * It then makes the ToolTip visible, which triggers * the ToolTipManager's showEffect if one is specified. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function showTip():void { // Dispatch a "showToolTip" event // from the object displaying the tooltip. var event:ToolTipEvent = new ToolTipEvent(ToolTipEvent.TOOL_TIP_SHOW); event.toolTip = currentToolTip; currentTarget.dispatchEvent(event); if (isError) { // Listen for a change event so we know when to hide the tip currentTarget.addEventListener(Event.CHANGE, changeHandler); } else { var sm:ISystemManager = getSystemManager(currentTarget); sm.addEventListener(MouseEvent.MOUSE_DOWN, systemManager_mouseDownHandler); } // Make the tooltip visible. // If showEffect exists, this effect will play. // When the effect ends, effectEndHandler() // will start the hideTimer. currentToolTip.visible = true; if (!showEffect) showEffectEnded(); } /** * Hides the current ToolTip. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function hideTip():void { // Dispatch a "hideToolTip" event // from the object that was displaying the tooltip. if (previousTarget) { var event:ToolTipEvent = new ToolTipEvent(ToolTipEvent.TOOL_TIP_HIDE); event.toolTip = currentToolTip; previousTarget.dispatchEvent(event); } // Make the tooltip invisible. // If hideEffect exists, this effect will play. // When the effect ends, effectEndHandler() // will reset the ToolTipManager to a no-tip state. if (currentToolTip) currentToolTip.visible = false; // When to do this? if (isError) { if (currentTarget) currentTarget.removeEventListener(Event.CHANGE, changeHandler); } else { if (previousTarget) { var sm:ISystemManager = getSystemManager(previousTarget); sm.removeEventListener(MouseEvent.MOUSE_DOWN, systemManager_mouseDownHandler); } } if (!hideEffect) hideEffectEnded(); } /** * Removes any currently visible ToolTip. * If the ToolTip is starting to show or hide, this method * removes the ToolTip immediately without completing the effect. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ mx_internal function reset():void { // Reset the three timers, in case any are running. showTimer.reset(); hideTimer.reset(); // If there is a current tooltip... if (currentToolTip) { // Remove the event handlers for the effectEnd of the showEffect // and hideEffect, so that calling endEffectsForTarget() doesn't // trigger effectEndHandler(). if (showEffect || hideEffect) { currentToolTip.removeEventListener(EffectEvent.EFFECT_END, effectEndHandler); } // End any show or hide effects that might be playing on it. EffectManager.endEffectsForTarget(currentToolTip); var e:DynamicEvent; if (hasEventListener("removeChild")) { e = new DynamicEvent("removeChild", false, true); e.sm = currentToolTip.systemManager; e.toolTip = currentToolTip; } if (!e || dispatchEvent(e)) { // Remove it. var sm:ISystemManager = currentToolTip.systemManager as ISystemManager; sm.topLevelSystemManager.toolTipChildren.removeChild(currentToolTip as DisplayObject); } currentToolTip = null; scrubTimer.delay = scrubDelay; scrubTimer.reset(); if (scrubDelay > 0) { scrubTimer.delay = scrubDelay; scrubTimer.start(); } } } /** * Creates an instance of the ToolTip class with the specified text * and displays it at the specified location in stage coordinates. * *

ToolTips appear in their own layer, on top of everything * except cursors.

* *

The standard way of using ToolTips is to let the ToolTipManager * automatically show and hide them as the user moves the mouse over * the objects that have the toolTip property set. * You can turn off this automatic ToolTip management by setting * the ToolTipManager's enabled property to * false.

* *

By contrast, this method—along with hideToolTip()—gives * you programmatic control over ToolTips. * You can show them when and where you choose, * and you can even show more than one at once if you need to. * (The ToolTipManager never does this, because it is generally * confusing to the user).

* *

This method first creates a new instance of ToolTip and calls the * addChild() method to put it into the SystemManager's * toolTips layer. * If you are showing an error tip, it sets the appropriate styles. * Then it sets the text for the ToolTip, sizes the ToolTip based on * its text, and positions it where you specified.

* *

You must save the reference to the ToolTip that this method * returns so that you can pass it to the hideToolTip() method.

* * @param text The text to display in the ToolTip instance. * * @param x The horizontal coordinate of the ToolTip in stage coordinates. * In case of multiple stages, the relevant stage is determined * from the context argument. * * @param y The vertical coordinate of the ToolTip in stage coordinates. * In case of multiple stages, the relevant stage is determined * from the context argument. * * @param errorTipBorderStyle The border style of an error tip. This method * argument can be null, "errorTipRight", "errorTipAbove", or "errorTipBelow". * If it is null, then the createToolTip() method creates a normal ToolTip. If it is * "errorTipRight", "errorTipAbove", or "errorTipBelow", then the createToolTip() * method creates an error tip, and this parameter determines where the arrow * of the error tip points to (the error's target). For example, if you pass "errorTipRight", Flex * positions the error tip (via the x and y arguments) to the * right of the error target; the arrow is on the left edge of the error tip. * * @param context This property is not currently used. * * @return The newly created ToolTip. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function createToolTip(text:String, x:Number, y:Number, errorTipBorderStyle:String = null, context:IUIComponent = null):IToolTip { var toolTip:ToolTip = new ToolTip(); var sm:ISystemManager = context ? context.systemManager as ISystemManager: FlexGlobals.topLevelApplication.systemManager as ISystemManager; if (context is IFlexModule) toolTip.moduleFactory = IFlexModule(context).moduleFactory; else toolTip.moduleFactory = sm; var e:DynamicEvent; if (hasEventListener("addChild")) { e = new DynamicEvent("addChild", false, true); e.sm = sm; e.toolTip = toolTip; } if (!e || dispatchEvent(e)) { sm.topLevelSystemManager.toolTipChildren.addChild(toolTip as DisplayObject); } if (errorTipBorderStyle) { toolTip.setStyle("styleName", "errorTip"); toolTip.setStyle("borderStyle", errorTipBorderStyle); } toolTip.text = text; sizeTip(toolTip); toolTip.move(x, y); // Ensure that tip is on screen? // Should x and y for error tip be tip of pointy border? // show effect? return toolTip as IToolTip; } /** * Destroys a specified ToolTip that was created by the createToolTip() method. * *

This method calls the removeChild() method to remove the specified * ToolTip from the SystemManager's ToolTips layer. * It will then be garbage-collected unless you keep a * reference to it.

* *

You should not call this method on the ToolTipManager's * currentToolTip.

* * @param toolTip The ToolTip instance to destroy. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function destroyToolTip(toolTip:IToolTip):void { var e:DynamicEvent; if (hasEventListener("removeChild")) { e = new DynamicEvent("removeChild", false, true); e.sm = toolTip.systemManager; e.toolTip = toolTip; } if (!e || dispatchEvent(e)) { // Remove it. var sm:ISystemManager = toolTip.systemManager as ISystemManager; sm.topLevelSystemManager.toolTipChildren.removeChild(toolTip as DisplayObject); } // hide effect? } /** * @private */ mx_internal function showEffectEnded():void { if (hideDelay == 0) { hideTip(); } else if (hideDelay < Infinity) { hideTimer.delay = hideDelay; hideTimer.start(); } if (currentTarget) { // Dispatch a "toolTipShown" event // from the object displaying the tooltip. var event:ToolTipEvent = new ToolTipEvent(ToolTipEvent.TOOL_TIP_SHOWN); event.toolTip = currentToolTip; currentTarget.dispatchEvent(event); } } /** * @private */ mx_internal function hideEffectEnded():void { reset(); // Dispatch a "toolTipEnd" event // from the object that was displaying the tooltip. if (previousTarget) { var event:ToolTipEvent = new ToolTipEvent(ToolTipEvent.TOOL_TIP_END); event.toolTip = currentToolTip; previousTarget.dispatchEvent(event); } } /** * @private */ mx_internal function getSystemManager( target:DisplayObject):ISystemManager { return target is IUIComponent ? IUIComponent(target).systemManager : null; } /** * @private */ private function getGlobalBounds(obj:DisplayObject, parent:DisplayObject, mirror:Boolean):Rectangle { var upperLeft:Point = new Point(0, 0); upperLeft = obj.localToGlobal(upperLeft); // If the layout has been mirrored, then the 0,0 is the uppper // right corner; compensate here. if (mirror) upperLeft.x -= obj.width; upperLeft = parent.globalToLocal(upperLeft); return new Rectangle(upperLeft.x, upperLeft.y, obj.width, obj.height); } //-------------------------------------------------------------------------- // // Event handlers // //-------------------------------------------------------------------------- /** * @private * This handler is called when the mouse moves over an object * with a toolTip. */ mx_internal function toolTipMouseOverHandler(event:MouseEvent):void { checkIfTargetChanged(DisplayObject(event.target)); } /** * @private * This handler is called when the mouse moves out of an object * with a toolTip. */ mx_internal function toolTipMouseOutHandler(event:MouseEvent):void { checkIfTargetChanged(event.relatedObject); } /** * @private * This handler is called when the mouse moves over an object * with an errorString. */ mx_internal function errorTipMouseOverHandler(event:MouseEvent):void { checkIfTargetChanged(DisplayObject(event.target)); } /** * @private * This handler is called when the mouse moves out of an object * with an errorString. */ mx_internal function errorTipMouseOutHandler(event:MouseEvent):void { checkIfTargetChanged(event.relatedObject); } /** * @private * This handler is called when the showTimer fires. * It creates the tooltip and starts its showEffect. */ mx_internal function showTimer_timerHandler(event:TimerEvent):void { // Make sure we still have a currentTarget when the timer fires. if (currentTarget) { createTip(); initializeTip(); positionTip(); showTip(); } } /** * @private * This handler is called when the hideTimer fires. * It starts the hideEffect. */ mx_internal function hideTimer_timerHandler(event:TimerEvent):void { hideTip(); } /** * @private * This handler is called when the showEffect or hideEffect ends. * When the showEffect ends, it starts the hideTimer, * which will automatically start hiding the tooltip when it fires, * even if the mouse is still over the target. * When the hideEffect ends, the tooltip is removed. */ mx_internal function effectEndHandler(event:EffectEvent):void { if (event.effectInstance.effect == showEffect) showEffectEnded(); else if (event.effectInstance.effect == hideEffect) hideEffectEnded(); } /** * @private * This handler is called when the user clicks the mouse * while a normal tooltip is displayed. * It immediately hides the tooltip. */ mx_internal function systemManager_mouseDownHandler(event:MouseEvent):void { reset(); } /** * @private */ mx_internal function changeHandler(event:Event):void { reset(); } } }