package flare.vis.controls { import flare.animate.Tween; import flare.display.TextSprite; import flare.vis.events.TooltipEvent; import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.InteractiveObject; import flash.display.Stage; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.filters.DropShadowFilter; import flash.geom.Rectangle; import flash.text.TextFormat; import flash.utils.Timer; [Event(name="show", type="flare.vis.events.TooltipEvent")] [Event(name="hide", type="flare.vis.events.TooltipEvent")] [Event(name="update", type="flare.vis.events.TooltipEvent")] /** * Interactive control for displaying a tooltip in response to mouse * hovers exceeding a minimum time interval. By default, a * flare.display.TextSprite instance is used to show a * tooltip. To change the tooltip text, clients can set either the * text or htmlText properties of this * TextSprite. For example: * *
	 * // create a new tooltip control and set the text
	 * var ttc:TooltipControl = new TooltipControl();
	 * TextSprite(ttc.tooltip).text = "The tooltip text";
	 * 
* *

Furthermore, this control fires events corresponding to tooltip show, * update (move), and hide events. Listeners can be added to dynamically * change the tooltip text when these events occur. Additionally, the * default text tooltip can be replaced with an arbitrary * DisplyObject to provide completely customized tooltips.

* * @see flare.vis.events.TooltipEvent * @see flare.display.TextSprite */ public class TooltipControl extends Control { private var _cur : DisplayObject; private var _showTimer : Timer; private var _hideTimer : Timer; private var _show : Boolean = false; private var _t : Tween; /** The tooltip delay, in milliseconds. */ public function get showDelay() : Number { return _showTimer.delay; } public function set showDelay(d : Number) : void { _showTimer.delay = d; } /** The delay before hiding a tooltip, in milliseconds. */ public function get hideDelay() : Number { return _hideTimer.delay; } public function set hideDelay(d : Number) : void { _hideTimer.delay = d; } /** The legal bounds for the tooltip in stage coordinates. * If null (the default), the full stage bounds are used. */ public var tipBounds : Rectangle = null; /** The x-offset from the mouse at which to place the tooltip. */ public var xOffset : Number = 0; /** The y-offset from the mouse at which to place the tooltip. */ public var yOffset : Number = 25; /** The display object presented as a tooltip. */ public var tooltip : DisplayObject = null; /** Duration of fade animations (in seconds) for tooltip show and hide. * If less than or equal to zero, no fade will be performed. */ public var fadeDuration : Number = 0.3; /** Indicates if the tooltip should follow the mouse pointer. */ public var followMouse : Boolean = true; // -------------------------------------------------------------------- /** * Creates a new TooltipControl. * @param filter a Boolean-valued filter function indicating which * items should receive tooltip handling */ public function TooltipControl(filter : *= null, tooltip : DisplayObject = null, show : Function = null, update : Function = null, hide : Function = null, delay : Number = 500) { this.filter = filter; _showTimer = new Timer(delay); _showTimer.addEventListener(TimerEvent.TIMER, onShow); _hideTimer = new Timer(100); _hideTimer.addEventListener(TimerEvent.TIMER, onHide); this.tooltip = tooltip ? tooltip : createDefaultTooltip(); if (show != null) addEventListener(TooltipEvent.SHOW, show); if (update != null) addEventListener(TooltipEvent.UPDATE, update); if (hide != null) addEventListener(TooltipEvent.HIDE, hide); } /** * Generates a default TextSprite tooltip * @return a new default tooltip object */ public static function createDefaultTooltip() : TextSprite { var fmt : TextFormat = new TextFormat("Arial", 14); fmt.leftMargin = 2; fmt.rightMargin = 2; var tip : TextSprite; tip = new TextSprite("", fmt); tip.textField.border = true; tip.textField.borderColor = 0; tip.textField.background = true; tip.textField.backgroundColor = 0xf5f5cc; tip.textField.multiline = true; tip.filters = [new DropShadowFilter(4, 45, 0, 0.5)]; return tip; } /** * Calculates the tooltip layout. * @param tip the tooltip object * @param obj the currently moused-over object */ protected function layout(tip : DisplayObject, obj : DisplayObject) : void { var s : Stage = tip.stage; tip.x = s.mouseX + xOffset; tip.y = s.mouseY + yOffset; var b : Rectangle = tipBounds ? tipBounds : getStageBounds(s); var r : Rectangle = tip.getBounds(s); if (r.width > b.width) { tip.x = b.left; } else if (r.left < b.left + 5) { tip.x = s.mouseX + xOffset; } else if (r.right > b.right - 5) { tip.x = s.mouseX - 2 - r.width; } if (r.height > b.height) { tip.y = b.top; } if (r.top < b.top + 5) { tip.y = s.mouseY - yOffset; } else if (r.bottom > b.bottom - 5) { tip.y = Math.max(b.top, s.mouseY - 7 - r.height); } } /** @private */ protected function fireEvent(type : String) : void { if (hasEventListener(type)) { dispatchEvent(new TooltipEvent(type, _cur, tooltip)); } } // -------------------------------------------------------------------- /** @inheritDoc */ public override function attach(obj : InteractiveObject) : void { if (!(obj is DisplayObjectContainer)) { throw new Error("TooltipControls can only be " + "attached to DisplayObjectContainers."); } super.attach(obj); if (obj != null) { obj.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver); obj.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut); } } /** @inheritDoc */ public override function detach() : InteractiveObject { if (_object != null) { _object.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver); _object.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut); } return super.detach(); } private function onMouseOver(evt : MouseEvent) : void { var n : DisplayObject = evt.target as DisplayObject; if (n == null || (_filter != null && !_filter(n))) return; _cur = n; if (_show) { _hideTimer.stop(); onShow(); } else { _showTimer.start(); } } private function onMouseMove(evt : MouseEvent) : void { if (!followMouse) { _object.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); return; } fireEvent(TooltipEvent.UPDATE); layout(tooltip, _cur); } private function onShow(evt : TimerEvent = null) : void { if (_t && _t.running) _t.stop(); _showTimer.stop(); _show = true; _cur.stage.addChild(tooltip); fireEvent(TooltipEvent.SHOW); layout(tooltip, _cur); if (fadeDuration <= 0) { tooltip.alpha = 1; tooltip.visible = true; } else { _t = new Tween(tooltip, fadeDuration, {alpha:1, visible:true}); _t.play(); } if (followMouse) _object.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); } private function onHide(evt : TimerEvent = null) : void { _hideTimer.stop(); fireEvent(TooltipEvent.HIDE); if (fadeDuration <= 0) { tooltip.alpha = 0; tooltip.visible = false; if (tooltip.parent) tooltip.parent.removeChild(tooltip); } else { _t = new Tween(tooltip, fadeDuration, {alpha: 0, visible: false}, true); _t.play(); } _show = false; _cur = null; } private function onMouseOut(evt : MouseEvent) : void { _showTimer.stop(); if (_cur == null) return; _object.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); if (_show) _hideTimer.start(); } // -------------------------------------------------------------------- private static function getStageBounds(stage : Stage) : Rectangle { return new Rectangle(0, 0, stage.stageWidth, stage.stageHeight); } } // end of class TooltipControl }