package com.exanimo.controls { import com.exanimo.controls.ButtonState; import com.exanimo.controls.IButton; import flash.display.FrameLabel; import flash.display.MovieClip; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.utils.Dictionary; /** * * Defines a ButtonClip: a button that uses frames for states instead of * separate Sprites. * * To do: * - eliminate redundant code! * * @langversion ActionScript 3 * @playerversion Flash 9.0.0 * * @author Matthew Tretter (matthew@exanimo.com) * @author Ryan Sprake * @version 2007.11.01 * */ public class ButtonClip extends MovieClip implements IButton { private var _state:String; private var _labels:Dictionary; private var _mouseIsDown:Boolean; private var _mouseIsOver:Boolean; private var _selected:Boolean; private var _toggle:Boolean; private var __hitArea:Sprite; /** * * constructor * */ public function ButtonClip() { this.buttonMode = true; this.mouseChildren = false; this._selected = false; this._labels = new Dictionary(true); for (var i:uint = 0; i < this.currentLabels.length; i++) { var label:FrameLabel = this.currentLabels[i]; var length:uint = (this.currentLabels[i + 1] ? this.currentLabels[i + 1].frame : this.totalFrames + 1) - label.frame; this._labels[label.name] = {frame: label.frame, length: length, lastFrame: label.frame + length - 1}; } this.__hitArea = this.getChildByName('_hitArea') as Sprite; if (this.__hitArea) { this.__hitArea.mouseEnabled = false; this.hitArea = this.__hitArea; } this.addEventListener(MouseEvent.CLICK, this._clickFilter); this.addEventListener(MouseEvent.CLICK, this._clickHandler); this.addEventListener(MouseEvent.ROLL_OVER, this._rollOverHandler); this.addEventListener(MouseEvent.ROLL_OUT, this._rollOutHandler); this.addEventListener(MouseEvent.MOUSE_DOWN, this._mouseDownHandler); if (this._labels[ButtonState.UP]) { this._gotoLastFrameOf(ButtonState.UP); } } // // accessors // /** * * Gets or sets a Boolean value that indicates whether a button can be * toggled. A value of true indicates that it can; a value of false * indicates that it cannot. * * If this value is true, clicking the button toggles it between * selected and unselected states. You can get or set this state * programmatically by using the selected property. * * If this value is false, the button does not stay pressed after the * user releases it. In this case, its selected property is always * false. * * Note: When the toggle is set to false, selected is forced to false * because only toggle buttons can be selected. * * The default value is false. * */ public function get toggle():Boolean { return this._toggle; } public function set toggle(toggle:Boolean):void { this._toggle = toggle; if (toggle) { this.selected = this.selected; } else { this.selected = false; } } /** * * * */ public override function get enabled():Boolean { return super.enabled; } public override function set enabled(enabled:Boolean):void { if (enabled == this.enabled) return; super.enabled = enabled; if (!enabled) { if (this._selected && this._labels[ButtonState.SELECTED_DISABLED]) { this._gotoAndPlay(ButtonState.SELECTED_DISABLED); } else if (this._labels[ButtonState.DISABLED]) { this._gotoAndPlay(ButtonState.DISABLED); } } else { if (this._mouseIsOver && this.selected && this._labels[ButtonState.SELECTED_OVER]) { this._gotoAndPlay(ButtonState.SELECTED_OVER); } else if (this._mouseIsOver && this._labels[ButtonState.OVER]) { this._gotoAndPlay(ButtonState.OVER); } else if (!this._mouseIsOver && this.selected && this._labels[ButtonState.SELECTED_UP]) { this._gotoAndPlay(ButtonState.SELECTED_UP); } else if (this._labels[ButtonState.UP]) { this._gotoAndPlay(ButtonState.UP); } } } /** * * * */ public function get selected():Boolean { return this._selected; } public function set selected(selected:Boolean):void { var dispatchChangeEvent:Boolean = selected != this.selected; this._selected = selected; if (selected) { if (this._mouseIsOver && this._labels[ButtonState.SELECTED_OVER]) { this._gotoAndPlay(ButtonState.SELECTED_OVER); } else if (this._mouseIsOver && this._labels[ButtonState.OVER]) { this._gotoAndPlay(ButtonState.OVER); } else if (this._labels[ButtonState.SELECTED_UP]) { this._gotoAndPlay(ButtonState.SELECTED_UP); } } else { if (this._mouseIsOver && this._labels[ButtonState.OVER]) { this._gotoAndPlay(ButtonState.OVER); } else if (this._labels[ButtonState.UP]) { this._gotoAndPlay(ButtonState.UP); } } if (dispatchChangeEvent) { this.dispatchEvent(new Event(Event.CHANGE)); } } /** * * Identifies the state of the clip. Possible values are in * ButtonState. * */ public function get state():String { return this._state; } // // public methods // /** * * * */ public override function gotoAndPlay(frame:Object, scene:String = null):void { this.addEventListener(Event.ENTER_FRAME, this._enterFrameHandler); super.gotoAndPlay(frame, scene) } // // private methods // /** * * Prevent click events if the button is disabled. * */ private function _clickFilter(e:MouseEvent):void { if (!this.enabled) { e.stopImmediatePropagation(); } } /** * * * */ private function _clickHandler(e:MouseEvent):void { if (this.toggle) { this.selected = !this.selected; } } /** * * * */ private function _enterFrameHandler(e:Event):void { if (this.currentFrame == this._labels[this.currentLabel].lastFrame) { this.stop(); this.removeEventListener(Event.ENTER_FRAME, this._enterFrameHandler); } } /** * * * */ private function _gotoAndPlay(frameName:String):void { if ((this.currentFrame < this._labels[frameName].frame) || (this.currentFrame > this._labels[frameName].lastFrame)) { this.gotoAndPlay(frameName); this._state = frameName; } } /** * * * */ private function _gotoLastFrameOf(buttonState:String):void { this.gotoAndStop(this._labels[buttonState].lastFrame); } /** * * * */ private function _mouseDownHandler(e:MouseEvent):void { this._mouseIsDown = true; if (!this.stage) return; if (!this.enabled) return; this.stage.addEventListener(MouseEvent.MOUSE_UP, this._mouseUpHandler); if (this.selected && this._labels[ButtonState.SELECTED_DOWN]) { this._gotoAndPlay(ButtonState.SELECTED_DOWN); } else if (this._labels[ButtonState.DOWN]) { this._gotoAndPlay(ButtonState.DOWN); } } /** * * * */ private function _mouseUpHandler(e:MouseEvent):void { e.currentTarget.removeEventListener(e.type, arguments.callee); this._mouseIsDown = false; if (!this.enabled || !this.mouseEnabled) return; if (this._mouseIsOver) { if (this.selected && this._labels[ButtonState.SELECTED_OVER]) { this._gotoAndPlay(ButtonState.SELECTED_OVER); } else if (this._labels[ButtonState.OVER]) { this._gotoAndPlay(ButtonState.OVER); } } else { if (this.selected && this._labels[ButtonState.SELECTED_UP]) { this._gotoAndPlay(ButtonState.SELECTED_UP); } else if (this._labels[ButtonState.UP]) { this._gotoAndPlay(ButtonState.UP); } } } /** * * * */ private function _rollOutHandler(e:MouseEvent):void { this._mouseIsOver = false; if (!this.enabled || !this.mouseEnabled) return; if (this.selected && this._labels[ButtonState.SELECTED_UP]) { this._gotoAndPlay(ButtonState.SELECTED_UP); } else if (this._labels[ButtonState.UP]) { this._gotoAndPlay(ButtonState.UP); } } /** * * * */ private function _rollOverHandler(e:MouseEvent):void { this._mouseIsOver = true; if (!this.enabled || !this.mouseEnabled) return; if (this._mouseIsDown && this.selected && this._labels[ButtonState.SELECTED_DOWN]) { this._gotoAndPlay(ButtonState.SELECTED_DOWN); } else if (this._mouseIsDown && this._labels[ButtonState.DOWN]) { this._gotoAndPlay(ButtonState.DOWN); } else if (this.selected && this._labels[ButtonState.SELECTED_OVER]) { this._gotoAndPlay(ButtonState.SELECTED_OVER); } else if (this._labels[ButtonState.OVER]) { this._gotoAndPlay(ButtonState.OVER); } } } }