Creating custom ToolTips

The ToolTipManager has two methods that let you programmatically use ToolTips. These methods are createToolTip() and destroyToolTip(), which you use to create and destroy new ToolTip objects. When you create a ToolTip object, you can customize it as you would any object, with access to its properties, styles, events, and effects.

The createToolTip() method has the following signature:

createToolTip(text:String, x:Number, y:Number, errorTipBorderStyle:String, 
    context:IUIComponent):IToolTip

The text parameter defines the contents of the ToolTip.

The x and y parameters define the x and y coordinates of the ToolTip, relative to the application container.

The errorTipBorderStyle parameter sets the location of the pointer on the error tip. This parameter is optional. If you specify the value of the errorTipBorderStyle parameter in the createToolTip() method, Flex styles the ToolTip as an error tip. Valid values are "errorTipRight", "errorTipAbove", or "errorTipBelow", and indicate the location of the error tip relative to the component. If you set the errorTipBorderStyle parameter to null, then the ToolTip is a normal ToolTip, not an error tip.

The following example shows the valid values and their resulting locations on the error tip:

The appearance of error tips in the right, bottom, and top locations.

The context parameter determines which StyleManager is used. Typically, you pass the object on which the ToolTip appears, so that the ToolTip's StyleManager is the same one use by that object. This object must be of type IUIComponent, so you might need to cast it in some cases, such as when you want to specify the context as the event.currentTarget in an event handler.

For more information about using error tips, see Using error tips.

The createToolTip() method returns a new ToolTip object that implements the IToolTip interface. You typically cast the return value of this method to a ToolTip, although it is more efficient if you do not do this. To cast, you can do one of the following:

  • Use the as keyword, as the following example shows:

    myTip = ToolTipManager.createToolTip(s,10,10) as ToolTip;
  • Use the type(object) casting syntax, as the following example shows:

    myTip = ToolTip(ToolTipManager.createToolTip(s,10,10));

These methods of casting differ only in the way that they behave when a cast fails.

Flex displays the ToolTip until you destroy it. In general, you should not display more than one ToolTip box at a time, because it is confusing to the user.

You can use the destroyToolTip() method to destroy the specified ToolTip object. The destroyToolTip() method has the following signature:

destroyToolTip(toolTip:IToolTip):void

The toolTip parameter is the ToolTip object that you want to destroy. This is the object returned by the createToolTip() method.

The following example creates a custom ToolTip when you move the mouse over a Panel container that contains three Button controls. Each Button control has its own ToolTip that appears when you mouse over that particular control. The big ToolTip disappears only when you move the mouse away from the Panel container.

<?xml version="1.0"?>
<!-- tooltips/CreatingToolTips.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009"    
    xmlns:mx="library://ns.adobe.com/flex/mx"     
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Script>
        <![CDATA[
            import mx.managers.ToolTipManager;
            import mx.controls.ToolTip;
            import mx.core.IUIComponent;

            public var myTip:ToolTip;

            private function createBigTip(event:Event):void {
                var s:String = "These buttons let you save, exit, or continue with the current operation."
                myTip = ToolTipManager.createToolTip(s,75,75,null,IUIComponent(event.currentTarget)) as ToolTip;
                myTip.setStyle("backgroundColor",0xFFCC00);
                myTip.width = 125;
                myTip.height = 75;
            }

            private function destroyBigTip():void {
                ToolTipManager.destroyToolTip(myTip);
            }
        ]]>
    </fx:Script>
    
    <fx:Style>
        @namespace s "library://ns.adobe.com/flex/spark";

        s|Panel {
            paddingLeft: 5;
            paddingRight: 5;
            paddingTop: 5;
            paddingBottom: 5;
        }
    </fx:Style>

    <s:Panel title="ToolTips" height="200" width="200" rollOver="createBigTip(event)" rollOut="destroyBigTip()">
        <s:Button label="OK" y="20" toolTip="Save your changes and exit."/>
        <s:Button label="Apply" y="50" toolTip="Apply changes and continue."/>
        <s:Button label="Cancel" y="80" toolTip="Cancel and exit."/>
    </s:Panel>

</s:Application>

The executing SWF file for the previous example is shown below:

Customizing the current ToolTip

Another approach to customizing ToolTip objects is to intercept the current ToolTip and customize it rather than using the ToolTipManager to create a new ToolTip object.

You do this by customizing the ToolTip during one of the ToolTip-related events like toolTipHide, toolTipShow, and toolTipShown.

The following example triggers the customization on the ToolTip’s toolTipShown event. This lets Flex handle all the logic for displaying and hiding the ToolTip, but lets you modify the current ToolTip’s text, position, and other properties.

<?xml version="1.0" encoding="utf-8"?>
<!-- tooltips/CustomToolTipInActionScript.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009"    
    xmlns:mx="library://ns.adobe.com/flex/mx"     
    xmlns:s="library://ns.adobe.com/flex/spark"
    creationComplete="initApp();">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Script>
        <![CDATA[
            import spark.components.Button;
            import mx.core.IToolTip;
            import mx.events.ToolTipEvent;
            import mx.managers.ToolTipManager;
            import mx.core.FlexGlobals;
            
            public var myTip:IToolTip;
            public var b:Button;
            
            private function initApp():void {           
                b = new Button();
                b.addEventListener("toolTipShown", createCustomTip);
                b.label = "Click Me";
                
                /* You must create a blank ToolTip so that the control
                   can dispatch ToolTip-related events. The new ToolTip
                   will replace this empty ToolTip. */
                b.toolTip = " ";
                
                addElement(b);
            }
            
            private function createCustomTip(e:ToolTipEvent):void {
                var s:String = "This is a ToolTip for the button.";
                myTip = ToolTipManager.currentToolTip;
                
                // Customize the text of the ToolTip.
                myTip.text = s; 
                
                // Customize the alpha of the ToolTip.
                myTip.alpha = .6;
                
                // Customize the position of the ToolTip.
                myTip.x = FlexGlobals.topLevelApplication.mouseX + 20;
                myTip.y = FlexGlobals.topLevelApplication.mouseY;
            }
        ]]>
    </fx:Script>
</s:Application>

The executing SWF file for the previous example is shown below:

Implementing the IToolTip interface

You can also create a custom ToolTip by extending an existing control, such as a Panel or other container, and implementing the IToolTip interface. The following example uses a Panel container as the base for a new implementation of the IToolTip interface:

<?xml version="1.0"?>
<!-- tooltips/ToolTipComponents/PanelToolTip.mxml -->
<s:Panel 
    xmlns:fx="http://ns.adobe.com/mxml/2009"    
    xmlns:mx="library://ns.adobe.com/flex/mx"     
    xmlns:s="library://ns.adobe.com/flex/spark"
    implements="mx.core.IToolTip" 
    width="200" 
    alpha=".75">
    <fx:Script>
        <![CDATA[
            [Bindable]
            public var bodyText:String = "";

            //  Implement required methods of the IToolTip interface; these 
            //  methods are not used in this example, though.
            public var _text:String;

            public function get text():String { 
                return _text; 
            } 
            public function set text(value:String):void {
            } 
        ]]>
    </fx:Script>

    <s:RichText text="{bodyText}" percentWidth="100"/>

</s:Panel>

In your application, you can create a custom ToolTip by intercepting the toolTipCreate event handler of the target component. In the event handler, you instantiate the new ToolTip and set its properties. You then point the toolTip property of the ToolTipEvent object to the new ToolTip.

In the following example, the first two buttons in the application use the custom PanelToolTip class in the ToolTipComponents package. The third button uses a default ToolTip to show you how the two are different. To run this example, store the PanelToolTip.mxml file in a subdirectory named ToolTipComponents.

<?xml version="1.0"?>
<!-- tooltips/MainCustomApp.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009"    
    xmlns:mx="library://ns.adobe.com/flex/mx"     
    xmlns:s="library://ns.adobe.com/flex/spark">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Script>
        <![CDATA[
            import ToolTipComponents.PanelToolTip;
            import mx.events.ToolTipEvent;

            private function createCustomTip(title:String, body:String, event:ToolTipEvent):void {
               var ptt:PanelToolTip = new PanelToolTip();
               ptt.title = title;
               ptt.bodyText = body;
               event.toolTip = ptt;
            }
        ]]>
    </fx:Script>
    
    <s:Button id="b1" 
        label="Delete" 
        toolTip=" " 
        toolTipCreate="createCustomTip('DELETE','Click this button to delete the report.', event)"
    /> 

    <s:Button id="b2" 
        label="Generate" 
        toolTip=" " 
        toolTipCreate="createCustomTip('GENERATE','Click this button to generate the report.', event)"
    /> 
    
    <s:Button id="b3"
        label="Stop"
        toolTip="Click this button to stop the creation of the report. This button uses a standard ToolTip style."
    />

</s:Application>

The executing SWF file for the previous example is shown below:

Positioning custom ToolTips

When you use the ToolTipManager to create a custom ToolTip, you specify the coordinates of the ToolTip on the Stage. You do this by specifying the values of the x and y parameters of the new ToolTip in the createToolTip() method. These coordinates are relative to the Stage. For example, a value of 0,0 creates a ToolTip at the top-left corner of the application.

In some cases, you might not know the exact position that you want the ToolTip to be drawn in; instead, you want the location of the ToolTip to be relative to the target component (the component that has a ToolTip on it). In those cases, you can use the location of the target component to calculate the values of these coordinates. For example, if you want the ToolTip to appear to a component’s right, you set the ToolTip’s x position to be the x position of the component plus the component’s width, plus some other value for an offset.

The following image shows the results of this formula:

A ToolTip on a TextInput control with a custom position.

You also set the value of the ToolTip’s y position to be the same as the target component’s y position to line the ToolTip and the component up horizontally.

One way to get the values you need to calculate the x position of the ToolTip is to use an event handler. Event objects passed to an event handler can give you the x position and the width of the target component.

The following example gets the value of the current target’s x, y, and width properties in the focusIn event handler, and uses them to position the ToolTip. In this case, the current target is the TextInput control, and the ToolTip appears to its right with a 10-pixel offset.

<?xml version="1.0"?>
<!-- tooltips/PlacingToolTips.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009"    
    xmlns:mx="library://ns.adobe.com/flex/mx"     
    xmlns:s="library://ns.adobe.com/flex/spark"
    height="100" width="300"> 

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Script> 
        <![CDATA[
             import mx.controls.ToolTip;
             import mx.managers.ToolTipManager;
             import mx.core.IUIComponent;
             
             private var tip:ToolTip;
             private var s:String;
               
             private function showTip(event:Object):void {
                s="My ToolTip";
                
                // Position the ToolTip to the right of the current target.
                tip = ToolTipManager.createToolTip(s, 
                    event.currentTarget.x + event.currentTarget.width + 10, 
                    event.currentTarget.y, null,IUIComponent(event.currentTarget)) 
                    as ToolTip;
            }

            private function destroyTip(event:Object):void {
                 ToolTipManager.destroyToolTip(tip);               
            }            
        ]]>
    </fx:Script>

    <s:TextInput id="a" 
        width="100" 
        focusIn="showTip(event)" 
        focusOut="destroyTip(event)"/>

    <s:TextInput id="b" 
        width="100" 
        focusIn="showTip(event)" 
        focusOut="destroyTip(event)"/>

</s:Application>

The executing SWF file for the previous example is shown below:

The previous example creates a ToolTip on a target component that is not inside any containers. However, in many cases, your components will be inside layout containers such as a VGroup or an HGroup. Under these circumstances, the coordinates you access in the event handler will be relative to the container and not the main application. But the ToolTipManager expects global coordinates when positioning the ToolTip. This will position ToolTips in unexpected locations.

To avoid this, you can use the contentToGlobal() method to convert the coordinates in the event handler from local to global. All components that subclass UIComponent have this method. It takes a single Point that is relative to the target’s enclosing container as an argument and returns a Point that is relative to the Stage.

The following example calls the TextInput control’s contentToGlobal() method to convert the control’s coordinates from those that are relative to the VGroup container to global coordinates.

<?xml version="1.0"?>
<!-- tooltips/PlacingToolTipsInContainers.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009"    
    xmlns:mx="library://ns.adobe.com/flex/mx"     
    xmlns:s="library://ns.adobe.com/flex/spark"
    height="400" width="600"> 

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Script> 
        <![CDATA[
            import mx.controls.ToolTip;
            import mx.managers.ToolTipManager;
            import mx.core.IUIComponent;

            private var tip:ToolTip;
            private var s:String;
               
            private function showTipA(event:Object):void {
                s="My Tip A";
                tip = ToolTipManager.createToolTip(s, 
                    event.currentTarget.x + event.currentTarget.width + 10, 
                    event.currentTarget.y) as ToolTip;
            }

            private function showTipB(event:Object):void {
                s="My Tip B";
                var pt:Point = new Point(0,0);
                
                /* Call this method to convert the object's
                   coordinates inside its container to the stage's 
                   global coordinates. */
                pt = event.currentTarget.contentToGlobal(pt);
                
                tip = ToolTipManager.createToolTip(s, 
                    pt.x + event.currentTarget.width + 10, pt.y, null,
                    IUIComponent(event.currentTarget)) as ToolTip;
            }

            private function destroyTip(event:Object):void {
                 ToolTipManager.destroyToolTip(tip);               
            }            
        ]]>
    </fx:Script>

    <!-- A ToolTip at the top level. -->
    <!-- The event handler for this ToolTip does not use any special 
    logic to account for whether the ToolTip is inside a container. 
    But this ToolTip is not inside a container so it positions itself 
    normally. -->
    <s:TextInput id="a"
        text="Good ToolTip placement"
        width="175" 
        focusIn="showTipA(event)" 
        focusOut="destroyTip(event)"/>

    <s:VGroup>
        <!-- A ToolTip inside a container. -->
        <!-- The event handler for this ToolTip accounts for the control 
        being inside a container and positions the ToolTip using the 
        contentToGlobal() method. -->
        <s:TextInput id="b" 
            text="Good ToolTip placement"
            width="175" 
            focusIn="showTipB(event)" 
            focusOut="destroyTip(event)"/>  

        <!-- A ToolTip inside a container. -->
        <!-- The event handler for this ToolTip does not use any special 
        logic to account for whether the ToolTip is inside a container. 
        As a result, it positions itself using coordinates that are relative
        to the container, but that are not converted to global coordinates. -->
        
        <s:TextInput id="c" 
            text="Bad ToolTip placement"
            width="175" 
            focusIn="showTipA(event)" 
            focusOut="destroyTip(event)"/>  

    </s:VGroup>
</s:Application>

The executing SWF file for the previous example is shown below: