PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/ui/source/temple/ui/form/components/ComboBox.as

http://templelibrary.googlecode.com/
ActionScript | 927 lines | 535 code | 103 blank | 289 comment | 58 complexity | a9aca94ed2f16a5f91aabec4413de3f0 MD5 | raw file
  1. /*
  2. * Temple Library for ActionScript 3.0
  3. * Copyright Š MediaMonks B.V.
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. All advertising materials mentioning features or use of this software
  14. * must display the following acknowledgement:
  15. * This product includes software developed by MediaMonks B.V.
  16. * 4. Neither the name of MediaMonks B.V. nor the
  17. * names of its contributors may be used to endorse or promote products
  18. * derived from this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY MEDIAMONKS B.V. ''AS IS'' AND ANY
  21. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  22. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23. * DISCLAIMED. IN NO EVENT SHALL MEDIAMONKS B.V. BE LIABLE FOR ANY
  24. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  25. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  26. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  27. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. *
  31. *
  32. * Note: This license does not apply to 3rd party classes inside the Temple
  33. * repository with their own license!
  34. */
  35. package temple.ui.form.components
  36. {
  37. import temple.common.interfaces.IHasValue;
  38. import temple.core.errors.TempleError;
  39. import temple.core.errors.throwError;
  40. import temple.data.collections.ICollection;
  41. import temple.ui.label.ILabel;
  42. import temple.ui.scroll.ScrollBehavior;
  43. import temple.ui.states.StateHelper;
  44. import temple.ui.states.open.IOpenState;
  45. import temple.utils.FrameDelay;
  46. import temple.utils.propertyproxy.IPropertyProxy;
  47. import temple.utils.types.DisplayObjectContainerUtils;
  48. import flash.display.DisplayObject;
  49. import flash.events.Event;
  50. import flash.events.FocusEvent;
  51. import flash.events.KeyboardEvent;
  52. import flash.events.MouseEvent;
  53. import flash.geom.Point;
  54. import flash.text.TextField;
  55. import flash.text.TextFieldType;
  56. import flash.ui.Keyboard;
  57. /**
  58. * A ComboBox let the user select from a (predefined) list of options.
  59. * A ComboBox is a combination of an InputField and a List. When the ComboBox is closed (collapsed),
  60. * the List is not visible. If the ComboBox is opened (expanded) the List is visible. The user
  61. * selects an option from the List by clicking on an item in the List.
  62. *
  63. * <p>The ComboBox extends the InputField and therefor it needs (at least) a TextField. I also needs
  64. * a List (or an object which implements IList). The TextField and List can be passed through the
  65. * constructor, when you create a ComboBox by code.</p>
  66. * <p>If you want to create a ComboBox in the Flash IDE you just need to add a TextField and a List
  67. * on the display list of the ComboBox. Make sure you set the class
  68. * (temple.ui.form.components.ComboBox) of the ComboBox as base class.</p>
  69. *
  70. * <p>A ComboBox can be used in a Form and supports ErrorStates, FocusStates and OpenStates.</p>
  71. *
  72. * <p>The ComboBox also support keyboard navigation. You can use the following keys to control the
  73. * ComboBox:
  74. * <ul>
  75. * <li>arrow keys: opens the ComboBox and steps through the options of the list.</li>
  76. * <li>space or enter: selects the focussed item of the list.</li>
  77. * <li>escape: closes the ComboBox</li>
  78. * <li>characters: selects an item of the list which matches the pressed character.</li>
  79. * </ul>
  80. * </p>
  81. *
  82. * @see temple.ui.form.components.InputField
  83. * @see temple.ui.form.components.List
  84. * @see temple.ui.form.components.IList
  85. * @see temple.ui.form.Form
  86. * @see temple.ui.states.error.IErrorState
  87. * @see temple.ui.states.focus.IFocusState
  88. * @see temple.ui.states.open.IOpenState
  89. *
  90. * @includeExample ./ComboBoxExample.as
  91. * @includeExample ./ComboBoxComponentExample.as
  92. * @includeExample ../FormExample.as
  93. *
  94. * @author Thijs Broerse
  95. */
  96. public class ComboBox extends InputField implements IList, ILabel
  97. {
  98. private var _list:IList;
  99. private var _isOpen:Boolean;
  100. private var _dataSource:ICollection;
  101. private var _openOnTop:Boolean = true;
  102. private var _autoClose:Boolean = true;
  103. private var _closeDelay:FrameDelay;
  104. private var _labelProxy:IPropertyProxy;
  105. /**
  106. * Creates a new ComboBox. If you create a ComboBox by code you must provide a TextField and a List.
  107. * If you create a ComboBox in the Flash IDE you can place a TextField and a List on the display list
  108. * of the ComboBox.
  109. * @param textField the TextField of the ComboBox, must be provided when creating a ComboBox in code.
  110. * @param list the list of the ComboBox, must be provided when creating a ComboBox in code.
  111. */
  112. public function ComboBox(textField:TextField = null, list:IList = null)
  113. {
  114. super(textField);
  115. if (list)
  116. {
  117. this._list = list;
  118. }
  119. else
  120. {
  121. this._list = DisplayObjectContainerUtils.findChildOfType(this, IList) as IList;
  122. }
  123. if (this._list == null) throwError(new TempleError(this, "No List found"));
  124. this.addEventListener(MouseEvent.CLICK, this.handleClick);
  125. this.addEventListener(MouseEvent.MOUSE_WHEEL, this.handleMouseWheel);
  126. this.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, this.handleMouseFocusChange);
  127. this.addEventListener(Event.REMOVED_FROM_STAGE, this.handleRemovedFromStage);
  128. this._list.addEventListener(Event.CHANGE, this.handleListChanged);
  129. this._list.addEventListener(MouseEvent.CLICK, this.handleClick);
  130. this._list.addEventListener(FocusEvent.FOCUS_IN, this.handleListEvent);
  131. this._list.addEventListener(FocusEvent.FOCUS_OUT, this.handleListEvent, false, -1);
  132. this._list.addEventListener(KeyboardEvent.KEY_DOWN, this.handleListEvent);
  133. this.close();
  134. this.submitOnEnter = false;
  135. this.selectTextOnFocus = false;
  136. }
  137. /**
  138. * Opens the drop-down list. Only opens if there are items in the list.
  139. */
  140. public function open():void
  141. {
  142. if (!this._list.length) return;
  143. if (this._closeDelay) this._closeDelay.destruct();
  144. if (!this._isOpen)
  145. {
  146. this._list.visible = true;
  147. this._isOpen = true;
  148. if (this._openOnTop && this.stage)
  149. {
  150. var position:Point = this.localToGlobal(new Point(this._list.x, this._list.y));
  151. this.stage.addChild(DisplayObject(this._list));
  152. this._list.position = position;
  153. }
  154. else
  155. {
  156. this.addChild(DisplayObject(this._list));
  157. }
  158. StateHelper.showState(this, IOpenState);
  159. this.dispatchEvent(new Event(Event.OPEN));
  160. }
  161. }
  162. /**
  163. * Closes the drop-down list.
  164. */
  165. public function close():void
  166. {
  167. if (this._list.focus) this.focus = true;
  168. this._isOpen = false;
  169. if (this.stage && this._list.parent == this.stage)
  170. {
  171. this._list.position = this.globalToLocal(new Point(this._list.x, this._list.y));
  172. }
  173. // remove list from display list to prevent its height being added to the height of the ComboBox
  174. if (this._list.parent) this._list.parent.removeChild(this._list as DisplayObject);
  175. StateHelper.hideState(this, IOpenState);
  176. this.dispatchEvent(new Event(Event.CLOSE));
  177. }
  178. /**
  179. * Gets a reference to the List component that the ComboBox component contains.
  180. */
  181. public function get list():IList
  182. {
  183. return this._list;
  184. }
  185. /**
  186. * @inheritDoc
  187. */
  188. public function addItem(data:*, label:String = null):void
  189. {
  190. this._list.addItem(data, label);
  191. }
  192. /**
  193. * @inheritDoc
  194. */
  195. public function addItemAt(data:*, index:uint, label:String = null):void
  196. {
  197. this._list.addItemAt(data, index, label);
  198. }
  199. /**
  200. * @inheritDoc
  201. */
  202. public function addItems(items:Array, labels:* = null):void
  203. {
  204. this._list.addItems(items, labels);
  205. }
  206. /**
  207. * @inheritDoc
  208. */
  209. public function hasItem(value:*):Boolean
  210. {
  211. return this._list.hasItem(value);
  212. }
  213. /**
  214. * @inheritDoc
  215. */
  216. public function getItem(value:*, fromIndex:int = 0):*
  217. {
  218. return this._list.getItem(value, fromIndex);
  219. }
  220. /**
  221. * @inheritDoc
  222. */
  223. public function getItemAt(index:uint):*
  224. {
  225. return this._list.getItemAt(index);
  226. }
  227. /**
  228. * @inheritDoc
  229. */
  230. public function setItemAt(data:*, index:uint, label:String = null):void
  231. {
  232. this._list.setItemAt(data, index, label);
  233. }
  234. /**
  235. * @inheritDoc
  236. */
  237. public function getLabel(value:*, fromIndex:int = 0):String
  238. {
  239. return this._list.getLabel(value, fromIndex);
  240. }
  241. /**
  242. * @inheritDoc
  243. */
  244. public function getLabelAt(index:uint):String
  245. {
  246. return this._list.getLabelAt(index);
  247. }
  248. /**
  249. * @inheritDoc
  250. */
  251. public function setLabelAt(index:uint, label:String):void
  252. {
  253. this._list.setLabelAt(index, label);
  254. if (this._list.selectedIndex == index)
  255. {
  256. if (this._labelProxy)
  257. {
  258. this._labelProxy.setValue(this, "text", IHasValue(this._list).value);
  259. }
  260. else
  261. {
  262. this.text = this._list.selectedLabel;
  263. }
  264. }
  265. }
  266. /**
  267. * @inheritDoc
  268. */
  269. public function isItemSelected(data:*, label:String = null):Boolean
  270. {
  271. return this._list.isItemSelected(data, label);
  272. }
  273. /**
  274. * @inheritDoc
  275. */
  276. public function isIndexSelected(index:uint):Boolean
  277. {
  278. return this._list.isIndexSelected(index);
  279. }
  280. /**
  281. * @inheritDoc
  282. */
  283. public function removeItem(data:*, label:String = null):Boolean
  284. {
  285. var success:Boolean = this._list.removeItem(data, label);
  286. if (!this.length) this.close();
  287. return success;
  288. }
  289. /**
  290. * @inheritDoc
  291. */
  292. public function removeItemAt(index:uint):Boolean
  293. {
  294. var success:Boolean = this._list.removeItemAt(index);
  295. if (!this.length) this.close();
  296. return success;
  297. }
  298. /**
  299. * @inheritDoc
  300. */
  301. public function removeAll():void
  302. {
  303. this._list.removeAll();
  304. this.reset();
  305. }
  306. /**
  307. * @inheritDoc
  308. */
  309. public function get selectedIndex():int
  310. {
  311. return this._list.selectedIndex;
  312. }
  313. /**
  314. * @inheritDoc
  315. */
  316. public function set selectedIndex(value:int):void
  317. {
  318. this._list.selectedIndex = value;
  319. }
  320. /**
  321. * @inheritDoc
  322. */
  323. public function get selectedItem():*
  324. {
  325. return this._list.selectedItem;
  326. }
  327. /**
  328. * @inheritDoc
  329. */
  330. public function set selectedItem(value:*):void
  331. {
  332. this._list.selectedItem = value;
  333. }
  334. /**
  335. * @inheritDoc
  336. */
  337. public function get selectedLabel():String
  338. {
  339. return this._list.selectedLabel;
  340. }
  341. /**
  342. * @inheritDoc
  343. */
  344. public function set selectedLabel(value:String):void
  345. {
  346. this._list.selectedLabel = value;
  347. }
  348. /**
  349. * @inheritDoc
  350. */
  351. public function get selectedItems():Array
  352. {
  353. return this._list.selectedItems;
  354. }
  355. /**
  356. * @inheritDoc
  357. */
  358. public function set selectedItems(value:Array):void
  359. {
  360. this._list.selectedItems = value;
  361. }
  362. /**
  363. * @inheritDoc
  364. */
  365. public function get selectedLabels():Array
  366. {
  367. return this._list.selectedLabels;
  368. }
  369. /**
  370. * @inheritDoc
  371. */
  372. public function set selectedLabels(value:Array):void
  373. {
  374. this._list.selectedLabels = value;
  375. }
  376. /**
  377. * @inheritDoc
  378. */
  379. public function get length():uint
  380. {
  381. return this._list.length;
  382. }
  383. /**
  384. * @inheritDoc
  385. */
  386. public function get allowMultipleSelection():Boolean
  387. {
  388. return this._list.allowMultipleSelection;
  389. }
  390. /**
  391. * @inheritDoc
  392. */
  393. [Inspectable(name="Allow multiple selection", type="Boolean", defaultValue=false)]
  394. public function set allowMultipleSelection(value:Boolean):void
  395. {
  396. this._list.allowMultipleSelection = value;
  397. }
  398. /**
  399. * @inheritDoc
  400. */
  401. public function get autoDeselect():Boolean
  402. {
  403. return this._list.autoDeselect;
  404. }
  405. /**
  406. * @inheritDoc
  407. */
  408. public function set autoDeselect(value:Boolean):void
  409. {
  410. this._list.autoDeselect = value;
  411. }
  412. /**
  413. * @inheritDoc
  414. */
  415. public function get rowHeight():Number
  416. {
  417. return this._list.rowHeight;
  418. }
  419. /**
  420. * @inheritDoc
  421. */
  422. public function set rowHeight(value:Number):void
  423. {
  424. this._list.rowHeight = value;
  425. }
  426. /**
  427. * @inheritDoc
  428. */
  429. public function get rowCount():uint
  430. {
  431. return this._list.rowCount;
  432. }
  433. /**
  434. * @inheritDoc
  435. */
  436. [Inspectable(name="Visible rows", type="Number", defaultValue=10)]
  437. public function set rowCount(value:uint):void
  438. {
  439. this._list.rowCount = value;
  440. }
  441. /**
  442. * @inheritDoc
  443. */
  444. public function sort(compareFunction:Function):void
  445. {
  446. this._list.sort(compareFunction);
  447. }
  448. /**
  449. * @inheritDoc
  450. */
  451. public function sortOn(names:*, options:* = 0, ...args:*):void
  452. {
  453. this._list.sortOn.apply(null, [names, options].concat(args));
  454. }
  455. /**
  456. * @inheritDoc
  457. */
  458. public function get wrapSearch():Boolean
  459. {
  460. return this._list.wrapSearch;
  461. }
  462. /**
  463. * @inheritDoc
  464. */
  465. [Inspectable(name="Wrap search", type="Boolean", defaultValue=true)]
  466. public function set wrapSearch(value:Boolean):void
  467. {
  468. this._list.wrapSearch = value;
  469. }
  470. /**
  471. * @inheritDoc
  472. */
  473. public function searchOnString(string:String, caseSensitive:Boolean = false):void
  474. {
  475. this._list.searchOnString(string, caseSensitive);
  476. }
  477. /**
  478. * @inheritDoc
  479. */
  480. public function get keySearch():Boolean
  481. {
  482. return this._list.keySearch;
  483. }
  484. /**
  485. * @inheritDoc
  486. */
  487. public function set keySearch(value:Boolean):void
  488. {
  489. this._list.keySearch = value;
  490. }
  491. /**
  492. * @inheritDoc
  493. */
  494. public function get scrollBehavior():ScrollBehavior
  495. {
  496. return this._list.scrollBehavior;
  497. }
  498. /**
  499. * @inheritDoc
  500. */
  501. [Inspectable(name="Enabled", type="Boolean", defaultValue=true)]
  502. override public function set enabled(value:Boolean):void
  503. {
  504. this.mouseEnabled = this.mouseChildren = this.textField.mouseEnabled = value;
  505. }
  506. /**
  507. * Gets or sets a Boolean value that indicates whether the ComboBox component is editable or read-only.
  508. * A value of true indicates that the ComboBox component is editable; a value of false indicates that it is not.
  509. *
  510. * <p>In an editable ComboBox component, a user can enter values into the text box that do not appear in the drop-down list.
  511. * The text box displays the text of the item in the list. If a ComboBox component is not editable, text cannot be entered into the text box.</p>
  512. */
  513. override public function get editable():Boolean
  514. {
  515. return super.editable;
  516. }
  517. /**
  518. * @inheritDoc
  519. */
  520. override public function get value():*
  521. {
  522. return this._list.selectedIndex != -1 ? (this._list as IHasValue).value : (this.editable ? super.value : null);
  523. }
  524. /**
  525. * @inheritDoc
  526. */
  527. override public function set value(value:*):void
  528. {
  529. (this._list as ISetValue).value = value;
  530. if (this.value != value)
  531. {
  532. super.value = value;
  533. }
  534. }
  535. /**
  536. * @inheritDoc
  537. */
  538. override public function reset():void
  539. {
  540. this._list.reset();
  541. super.reset();
  542. this.close();
  543. }
  544. /**
  545. * The colletion that is used to fill the ComboBox
  546. */
  547. public function get dataSource():ICollection
  548. {
  549. return this._dataSource;
  550. }
  551. /**
  552. * @private
  553. */
  554. [Collection(name="Data source", collectionClass="temple.data.collections.Collection", collectionItem="temple.ui.form.components.ListItemData",identifier="label")]
  555. public function set dataSource(value:ICollection):void
  556. {
  557. this._dataSource = value;
  558. var leni:int = this._dataSource.length;
  559. for (var i:int = 0; i < leni ; i++)
  560. {
  561. this.addItem(this._dataSource.getItemAt(i));
  562. }
  563. }
  564. /**
  565. * @inheritDoc
  566. *
  567. * Overridden to get rid of Inspectable
  568. */
  569. override public function set submitOnEnter(value:Boolean):void
  570. {
  571. super.submitOnEnter = value;
  572. }
  573. /**
  574. * @inheritDoc
  575. *
  576. * Overridden to get rid of Inspectable
  577. */
  578. override public function set prefillText(value:String):void
  579. {
  580. super.prefillText = value;
  581. }
  582. /**
  583. * A Boolean that indicates if the List should open on top of everything else on the stage.
  584. * @default true
  585. */
  586. public function get openOnTop():Boolean
  587. {
  588. return this._openOnTop;
  589. }
  590. /**
  591. * @private
  592. */
  593. public function set openOnTop(value:Boolean):void
  594. {
  595. this._openOnTop = value;
  596. }
  597. /**
  598. * A Boolean which indicates if the ComboBox should close if an item in the List is selected.
  599. */
  600. public function get autoClose():Boolean
  601. {
  602. return this._autoClose;
  603. }
  604. /**
  605. * @private
  606. */
  607. public function set autoClose(value:Boolean):void
  608. {
  609. this._autoClose = value;
  610. }
  611. /**
  612. * Proxy for setting the label of the ComboBox based on the value of the list
  613. */
  614. public function get labelProxy():IPropertyProxy
  615. {
  616. return this._labelProxy;
  617. }
  618. /**
  619. * @private
  620. */
  621. public function set labelProxy(value:IPropertyProxy):void
  622. {
  623. this._labelProxy = value;
  624. }
  625. /**
  626. * @inheritDoc
  627. */
  628. public function get label():String
  629. {
  630. return this.text;
  631. }
  632. /**
  633. * @inheritDoc
  634. */
  635. public function set label(value:String):void
  636. {
  637. this.text = value;
  638. }
  639. /**
  640. * A Boolean which indicates if the ComboBox is currently opened.
  641. */
  642. public function get isOpen():Boolean
  643. {
  644. return this._isOpen;
  645. }
  646. /**
  647. * @inheritDoc
  648. */
  649. public function selectItem(data:*):Boolean
  650. {
  651. return this._list.selectItem(data);
  652. }
  653. /**
  654. * @inheritDoc
  655. */
  656. public function deselectItem(data:*):Boolean
  657. {
  658. return this._list.deselectItem(data);
  659. }
  660. override protected function updateHint():void
  661. {
  662. if (!this.focus || !this._showsHint || this.textField.type == TextFieldType.INPUT)
  663. {
  664. super.updateHint();
  665. }
  666. }
  667. protected function handleListChanged(event:Event):void
  668. {
  669. if (this.isDestructed) return;
  670. if (this._list.selectedIndex != -1)
  671. {
  672. if (this._labelProxy)
  673. {
  674. this._labelProxy.setValue(this, "text", IHasValue(this._list).value);
  675. }
  676. else
  677. {
  678. this.text = this._list.selectedLabel;
  679. }
  680. }
  681. else
  682. {
  683. this.dispatchEvent(new Event(Event.CHANGE));
  684. if (this.submitOnChange)
  685. {
  686. this.dispatchEvent(new FormElementEvent(FormElementEvent.SUBMIT));
  687. }
  688. }
  689. }
  690. override protected function handleKeyDown(event:KeyboardEvent):void
  691. {
  692. super.handleKeyDown(event);
  693. switch (event.keyCode)
  694. {
  695. case Keyboard.SPACE:
  696. case Keyboard.ENTER:
  697. {
  698. event.stopPropagation();
  699. this.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, false, NaN, NaN, null, event.ctrlKey, event.altKey, event.shiftKey));
  700. break;
  701. }
  702. case Keyboard.UP:
  703. case Keyboard.DOWN:
  704. {
  705. if (!this._list.focus)
  706. {
  707. event.stopPropagation();
  708. this.open();
  709. this._list.focus = true;
  710. }
  711. break;
  712. }
  713. case Keyboard.ESCAPE:
  714. {
  715. event.stopPropagation();
  716. this.close();
  717. break;
  718. }
  719. default:
  720. {
  721. if (!this._list.focus && this.keySearch)
  722. {
  723. event.preventDefault();
  724. // search for an item starting with this character
  725. this.searchOnString(String.fromCharCode(event.charCode).toUpperCase());
  726. break;
  727. }
  728. }
  729. }
  730. }
  731. private function handleListEvent(event:Event):void
  732. {
  733. if (!this.contains(this._list as DisplayObject))
  734. {
  735. this.dispatchEvent(event.clone());
  736. }
  737. }
  738. protected function handleClick(event:MouseEvent):void
  739. {
  740. if (this._autoClose && event.currentTarget == this._list && event.target is IListRow || !this._list.contains(event.target as DisplayObject))
  741. {
  742. if (this._isOpen)
  743. {
  744. this.close();
  745. }
  746. else
  747. {
  748. this.open();
  749. }
  750. }
  751. }
  752. protected function handleMouseWheel(event:MouseEvent):void
  753. {
  754. if (event.target is DisplayObject && (event.target == this._list || this._list.contains(event.target as DisplayObject))) return;
  755. if (event.delta > 0)
  756. {
  757. this._list.selectedIndex--;
  758. }
  759. else
  760. {
  761. this._list.selectedIndex++;
  762. }
  763. event.stopPropagation();
  764. }
  765. protected function handleListFocusChange(event:FocusEvent):void
  766. {
  767. event.stopImmediatePropagation();
  768. this.open();
  769. }
  770. override protected function handleFocusIn(event:FocusEvent):void
  771. {
  772. super.handleFocusIn(event);
  773. if (this._closeDelay) this._closeDelay.destruct();
  774. }
  775. override protected function handleFocusOut(event:FocusEvent):void
  776. {
  777. if (event.relatedObject == null || !this.contains(event.relatedObject) && !this._list.contains(event.relatedObject))
  778. {
  779. super.handleFocusOut(event);
  780. if (this._autoClose)
  781. {
  782. if (this._closeDelay) this._closeDelay.destruct();
  783. this._closeDelay = new FrameDelay(this.close);
  784. }
  785. }
  786. else
  787. {
  788. event.stopImmediatePropagation();
  789. }
  790. }
  791. private function handleMouseFocusChange(event:FocusEvent):void
  792. {
  793. event.preventDefault();
  794. }
  795. private function handleRemovedFromStage(event:Event):void
  796. {
  797. if (this._isOpen)
  798. {
  799. this.close();
  800. }
  801. }
  802. /**
  803. * @inheritDoc
  804. */
  805. override public function destruct():void
  806. {
  807. this.removeAllStrongEventListenersForType(Event.CHANGE);
  808. if (this._list && !this._list.isDestructed)
  809. {
  810. this._list.removeEventListener(Event.CHANGE, this.handleListChanged);
  811. this._list.destruct();
  812. this._list = null;
  813. }
  814. if (this._closeDelay)
  815. {
  816. this._closeDelay.destruct();
  817. this._closeDelay = null;
  818. }
  819. this._dataSource = null;
  820. this._labelProxy = null;
  821. super.destruct();
  822. }
  823. }
  824. }