PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/CCJ/src/com/godpaper/business/managers/ToolTipsManager.as

http://chinesechessjam.googlecode.com/
ActionScript | 377 lines | 211 code | 29 blank | 137 comment | 51 complexity | 95ba5d239107f470c0a2ce4f95c6af7d MD5 | raw file
  1. package com.godpaper.business.managers
  2. {
  3. import flash.display.DisplayObject;
  4. import flash.events.Event;
  5. import flash.events.IEventDispatcher;
  6. import flash.geom.Point;
  7. import flash.utils.Dictionary;
  8. import mx.controls.ToolTip;
  9. import mx.core.IToolTip;
  10. import mx.core.UIComponent;
  11. import mx.events.MoveEvent;
  12. import mx.events.ResizeEvent;
  13. import mx.events.ToolTipEvent;
  14. import mx.events.ValidationResultEvent;
  15. import mx.managers.ToolTipManager;
  16. import mx.validators.Validator;
  17. /**
  18. * This class makes the error ToolTip shown up all the time instead of
  19. * just when the mouse is over the target component.
  20. * It is designed to work with a Validator control, but you can manually use this class
  21. * by calling the showErrorTip() and hideErrorTip() functions too.
  22. * <br>
  23. * When the showErrorTip(target:Object, error:String) function is called, if the error String is null and
  24. * the target is a UIComponent then the UIComponent.errorString property is used in the error tip.
  25. * <br>
  26. * Here are some more resources on the issue:<br>
  27. * <li><a href="http://bugs.adobe.com/jira/browse/SDK-11256">Adobe Bug Tracker</a></li>
  28. * <li><a href="http://aralbalkan.com/1125">Aral Balkan - Better form validation in Flex</a></li>
  29. * <li><a href="http://blog.flexmonkeypatches.com/2007/09/17/using-the-flex-tooltip-manager-to-create-error-tooltips-and-position-them/">Creating Error Tooltips</a></li>
  30. *
  31. * @author Chris Callendar
  32. * @date August 5th, 2009
  33. */
  34. public class ToolTipsManager
  35. {
  36. // maps the target components to the error IToolTip components
  37. private static var errorTips:Dictionary = new Dictionary();
  38. // maps the validators to a boolean indicating whether the toolTipShown even listener has been
  39. // added to the validator source property.
  40. private static var validators:Dictionary = new Dictionary();
  41. // maps the popUps to an Array of validators
  42. private static var popUps:Dictionary = new Dictionary();
  43. /**
  44. * Adds "invalid" and "valid" event listeners which show and hide the error tooltips.
  45. */
  46. public static function registerValidator(validator:Validator):void {
  47. validator.addEventListener(ValidationResultEvent.VALID, validHandler);
  48. validator.addEventListener(ValidationResultEvent.INVALID, invalidHandler);
  49. validators[validator] = false;
  50. // Also listen for when the real mouse over error tooltip is shown
  51. addValidatorSourceListeners(validator);
  52. }
  53. /**
  54. * Removes the "invalid" and "valid" event listeners from the validator.
  55. * Also removes the error tip.
  56. */
  57. public static function unregisterValidator(validator:Validator):void {
  58. validator.removeEventListener(ValidationResultEvent.VALID, validHandler);
  59. validator.removeEventListener(ValidationResultEvent.INVALID, invalidHandler);
  60. // make sure our error tooltip is hidden
  61. removeErrorTip(validator.source);
  62. // stop listening for events on the validator's source
  63. removeValidatorSourceListeners(validator);
  64. }
  65. /**
  66. * Registers the validator (see registerValidator), and adds MOVE and RESIZE listeners
  67. * on the popUp component to keep the error tip positioned properly.
  68. * It can also hide all existing error tips which is a good idea when showing a popUp
  69. * because the error tips will appear on top of the popUp window.
  70. * @param validator the validator to register
  71. * @param popUp the popUp component which will have move and resize listeners added to
  72. * @param hideExistingErrorTips if true then all existing error tips will be hidden
  73. */
  74. public static function registerValidatorOnPopUp(validator:Validator, popUp:UIComponent,
  75. hideExistingErrorTips:Boolean = false):void {
  76. // hide all existing error tips to prevent them from being on top of the popUp
  77. if (hideExistingErrorTips) {
  78. hideAllErrorTips();
  79. }
  80. registerValidator(validator);
  81. if (popUps[popUp] == null) {
  82. popUps[popUp] = [];
  83. // add move/resize listeners on the popUp to keep the error tip positioned properly
  84. popUp.addEventListener(MoveEvent.MOVE, targetMoved);
  85. popUp.addEventListener(ResizeEvent.RESIZE, targetMoved);
  86. }
  87. var validators:Array = (popUps[popUp] as Array);
  88. if (validators.indexOf(validator) == -1) {
  89. validators.push(validator);
  90. }
  91. }
  92. /**
  93. * Unregisters all the validators that are associated with the given popup.
  94. * Also removes the MOVE and RESIZE listeners on the popUp.
  95. * It can also re-validate all existing validators which will show the error tips if necessary.
  96. * @param popUp the popUp component which will have move and resize listeners added to
  97. * @param validateExistingErrorTips if true then all other validators will be validated
  98. */
  99. public static function unregisterPopUpValidators(popUp:UIComponent, validateExistingErrorTips:Boolean = false):void {
  100. if (popUps[popUp] != null) {
  101. var validators:Array = (popUps[popUp] as Array);
  102. for each (var validator:Validator in validators) {
  103. unregisterValidator(validator);
  104. }
  105. delete popUps[popUp];
  106. // remove the move/resize listeners on the popUp
  107. popUp.removeEventListener(MoveEvent.MOVE, targetMoved);
  108. popUp.removeEventListener(ResizeEvent.RESIZE, targetMoved);
  109. }
  110. // show any error tips that were showing before the popUp was shown
  111. if (validateExistingErrorTips) {
  112. validateAll();
  113. }
  114. }
  115. /**
  116. * Adds the ToolTipEvent.TOOL_TIP_SHOW event listener on the validator's source
  117. * only if it hasn't already been added.
  118. */
  119. private static function addValidatorSourceListeners(validator:Validator):void {
  120. // make sure the listeners have been added
  121. if (validator) {
  122. var alreadyAdded:Boolean = validators[validator];
  123. if (!alreadyAdded && (validator.source is IEventDispatcher)) {
  124. var ed:IEventDispatcher = (validator.source as IEventDispatcher);
  125. // need to listener for when the real tooltip gets shown
  126. // we'll hide it if is an error tooltip since we are already showing it
  127. ed.addEventListener(ToolTipEvent.TOOL_TIP_SHOWN, toolTipShown);
  128. // also need to listen for move and resize events to keep the error tip positioned correctly
  129. ed.addEventListener(MoveEvent.MOVE, targetMoved);
  130. ed.addEventListener(ResizeEvent.RESIZE, targetMoved);
  131. validators[validator] = true;
  132. }
  133. }
  134. }
  135. /**
  136. * Removes the event listeners that were added to the validator's source.
  137. */
  138. private static function removeValidatorSourceListeners(validator:Validator):void {
  139. if (validator && (validators[validator] == true)) {
  140. if (validator.source is IEventDispatcher) {
  141. var ed:IEventDispatcher = (validator.source as IEventDispatcher);
  142. ed.removeEventListener(ToolTipEvent.TOOL_TIP_SHOWN, toolTipShown);
  143. ed.removeEventListener(MoveEvent.MOVE, targetMoved);
  144. ed.removeEventListener(ResizeEvent.RESIZE, targetMoved);
  145. }
  146. delete validators[validator];
  147. }
  148. }
  149. /**
  150. * Called when the validator fires the valid event.
  151. * Hides the error tooltip if it is visible.
  152. */
  153. public static function validHandler(event:ValidationResultEvent):void {
  154. // the target component is valid, so hide the error tooltip
  155. var validator:Validator = Validator(event.target);
  156. hideErrorTip(validator.source);
  157. // ensure that the source listeners were added
  158. addValidatorSourceListeners(validator);
  159. }
  160. /**
  161. * Called when the validator fires an invalid event.
  162. * Shows the error tooltip with the ValidatorResultEvent.message as the error String.
  163. */
  164. public static function invalidHandler(event:ValidationResultEvent):void {
  165. // the target component is invalid, so show the error tooltip
  166. var validator:Validator = Validator(event.target);
  167. showErrorTip(validator.source, event.message);
  168. // ensure that the source listeners were added
  169. addValidatorSourceListeners(validator);
  170. }
  171. /**
  172. * When the target component moves or is resized we need to keep the
  173. * error tip in the correct position.
  174. */
  175. private static function targetMoved(event:Event):void {
  176. var target:DisplayObject = (event.target as DisplayObject);
  177. // check if the target is actually a popUp, in which case we get the real
  178. // target from the validator source
  179. if (popUps[target] != null) {
  180. var validators:Array = (popUps[target] as Array);
  181. for each (var validator:Validator in validators) {
  182. var source:DisplayObject = (validator.source as DisplayObject);
  183. handleTargetMoved(source);
  184. }
  185. } else {
  186. handleTargetMoved(target);
  187. }
  188. }
  189. private static function handleTargetMoved(target:DisplayObject):void {
  190. if (target is UIComponent) {
  191. // need to wait for move/resize to finish
  192. UIComponent(target).callLater(updateErrorTipPosition, [ target ]);
  193. } else {
  194. updateErrorTipPosition(target);
  195. }
  196. }
  197. /**
  198. * Movies the error tip for the given target.
  199. */
  200. public static function updateErrorTipPosition(target:Object):void {
  201. var errorTip:IToolTip = getErrorTip(target);
  202. if (errorTip) {
  203. var pos:Point = getErrorTipPosition(target as DisplayObject);
  204. errorTip.move(pos.x, pos.y);
  205. }
  206. }
  207. /**
  208. * This gets called when the mouse hovers over the target component
  209. * and a tooltip is shown - either a normal tooltip or an error tooltip.
  210. * If the tooltip is an error tooltip and our error tooltip is already showing
  211. * then we hide this new tooltip immediately.
  212. */
  213. private static function toolTipShown(event:ToolTipEvent):void {
  214. // hide our error tip until this tooltip is hidden
  215. var style:Object = ToolTip(event.toolTip).styleName;
  216. if ((style == "errorTip") && (getErrorTip(event.target) != null)) {
  217. // hide this tooltip, ours is already displaying (or is about to display)
  218. event.toolTip.visible = false;
  219. event.toolTip.width = 0;
  220. event.toolTip.height = 0;
  221. event.currentTarget.dispatchEvent(new ToolTipEvent(ToolTipEvent.TOOL_TIP_HIDE, false, false, event.toolTip));
  222. }
  223. }
  224. /**
  225. * Gets the cached IToolTip object for the given target.
  226. */
  227. public static function getErrorTip(target:Object):IToolTip {
  228. return (target ? errorTips[target] as IToolTip : null);
  229. }
  230. /**
  231. * Determines if the error tooltip exists and if it is visible.
  232. */
  233. public static function isErrorTipVisible(target:Object):Boolean {
  234. var errorTip:IToolTip = getErrorTip(target);
  235. return (errorTip && errorTip.visible);
  236. }
  237. /**
  238. * Creates the error IToolTip object if one doesn't already exist for the given target.
  239. * If the error tooltip already exists then the error string is updated on the existing tooltip.
  240. * The tooltip will not be shown if the error (or errorString) is blank.
  241. * @param target the target component (usually a UIComponent)
  242. * @param error the optional error String, if null and the target is a UIComponent then
  243. * the target.errorString property is used.
  244. */
  245. public static function createErrorTip(target:Object, error:String = null):IToolTip {
  246. var errorTip:IToolTip = null;
  247. var position:Point;
  248. if (target) {
  249. // use the errorString property on the target
  250. if ((error == null) && (target is UIComponent)) {
  251. error = (target as UIComponent).errorString;
  252. }
  253. errorTip = getErrorTip(target);
  254. if (!errorTip) {
  255. if ((error != null) && (error.length > 0)) {
  256. position = getErrorTipPosition(target as DisplayObject);
  257. errorTip = ToolTipManager.createToolTip(error, position.x, position.y);
  258. errorTips[target] = errorTip;
  259. // set the styles to match the real error tooltip
  260. var tt:ToolTip = ToolTip(errorTip);
  261. tt.styleName = "errorTip";
  262. }
  263. } else if ((error != null) && (error != errorTip.text)) {
  264. // update the error tooltip text
  265. errorTip.text = error;
  266. // update the position too
  267. position = getErrorTipPosition(target as DisplayObject);
  268. errorTip.move(position.x, position.y);
  269. }
  270. }
  271. return errorTip;
  272. }
  273. /**
  274. * Gets the position for the tooltip in global coordinates.
  275. */
  276. private static function getErrorTipPosition(target:DisplayObject):Point {
  277. // position the error tip to be in the exact same position as the real error tooltip
  278. var pt:Point = new Point();
  279. if (target) {
  280. // need to get the position of the target in global coordinates
  281. var global:Point = target.localToGlobal(new Point(0, 0));
  282. // position on the right side of the target
  283. pt.x = global.x + target.width + 4;
  284. pt.y = global.y - 1;
  285. }
  286. return pt;
  287. }
  288. /**
  289. * Creates the error tooltip if it doesn't already exist, and makes it visible.
  290. */
  291. public static function showErrorTip(target:Object, error:String = null):void {
  292. var errorTip:IToolTip = createErrorTip(target, error);
  293. if (errorTip) {
  294. errorTip.visible = true;
  295. }
  296. }
  297. /**
  298. * Hides the existing error tooltip for the target if one exists.
  299. */
  300. public static function hideErrorTip(target:Object, clearErrorString:Boolean = false):void {
  301. var errorTip:IToolTip = getErrorTip(target);
  302. if (errorTip) {
  303. errorTip.visible = false;
  304. }
  305. // clear the errorString property to remove the red border around the target control
  306. if (clearErrorString && target && target.hasOwnProperty("errorString")) {
  307. target.errorString = "";
  308. }
  309. }
  310. /**
  311. * Hides the error tooltip for the target AND removes it from the
  312. * ToolTipManager (by calling ToolTipManager.destroyToolTip).
  313. */
  314. public static function removeErrorTip(target:Object):void {
  315. var errorTip:IToolTip = getErrorTip(target);
  316. if (errorTip) {
  317. errorTip.visible = false;
  318. ToolTipManager.destroyToolTip(errorTip);
  319. delete errorTips[target];
  320. }
  321. }
  322. /**
  323. * Hides all the error tips.
  324. */
  325. public static function hideAllErrorTips():void {
  326. for (var target:Object in errorTips) {
  327. hideErrorTip(target, false);
  328. }
  329. }
  330. /**
  331. * Shows all the error tips - doesn't check to see if an error string is set!
  332. */
  333. public static function showAllErrorTips():void {
  334. for (var target:Object in errorTips) {
  335. showErrorTip(target);
  336. }
  337. }
  338. /**
  339. * Calls validate() on all the validators.
  340. */
  341. public static function validateAll():void {
  342. // need to validator to figure out which error tips should be shown
  343. for (var validator:Object in validators) {
  344. validator.validate();
  345. }
  346. }
  347. }
  348. }