Why Isn't MouseEvent.ROLL_OVER/OUT Firing Consistantly? I Just Want to Do a Simple Toggle!

I working on a VideoScreen component, which at this point just extends UIComponent and contains a Video object as its first child. After the Video is added to the stage, I create an overlay using a Sprite object. The overlay isn't added to the stage until the user does a MouseEvent.ROLL_OVER the VideoScreen component. The overlay is removed from the stage on MouseEvent.ROLL_OUT.

The problem here is that sometimes the MouseEvent.ROLL_OVER doesn't fire, which throws the toggle off (ROLL_OUT will add the overlay to the stage instead of removing it). Can you help me figure this out?

Here is some of my debugging output; in this case the error happens just after COUNT 2 where two 'ROLL_OUT' events are fired without a 'ROLL_OVER' between them:

----------------------------------------------------------
VideoScreen mouseRollOverOutListener COUNT: 1      (Odd numbers should always be MouseEvent type='rollOver')
[MouseEvent type="rollOver" bubbles=false cancelable=false eventPhase=2 localX=16 localY=62 stageX=16 stageY=62 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
Showing overlay (expecting FALSE)...      _isShowingOverlay current value: false
Overlay should now be TRUE      _isShowingOverlay: true

VideoScreen mouseRollOverOutListener COUNT: 2      (Odd numbers should always be MouseEvent type='rollOver')
[MouseEvent type="rollOut" bubbles=false cancelable=false eventPhase=2 localX=-1 localY=-1 stageX=-1 stageY=-1 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
Hiding overlay (expecting TRUE)...      _isShowingOverlay current value: true
Overlay should now be FALSE      _isShowingOverlay: false

VideoScreen mouseRollOverOutListener COUNT: 3      (Odd numbers should always be MouseEvent type='rollOver')
[MouseEvent type="rollOut" bubbles=false cancelable=false eventPhase=2 localX=-1 localY=-1 stageX=-1 stageY=-1 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
Showing overlay (expecting FALSE)...      _isShowingOverlay current value: false
Overlay should now be TRUE      _isShowingOverlay: true

VideoScreen mouseRollOverOutListener COUNT: 4      (Odd numbers should always be MouseEvent type='rollOver')
[MouseEvent type="rollOver" bubbles=false cancelable=false eventPhase=2 localX=2 localY=69 stageX=2 stageY=69 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
Hiding overlay (expecting TRUE)...      _isShowingOverlay current value: true
Overlay should now be FALSE      _isShowingOverlay: false

VideoScreen mouseRollOverOutListener COUNT: 5      (Odd numbers should always be MouseEvent type='rollOver')
[MouseEvent type="rollOut" bubbles=false cancelable=false eventPhase=2 localX=-1 localY=-1 stageX=-1 stageY=-1 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
Showing overlay (expecting FALSE)...      _isShowingOverlay current value: false
Overlay should now be TRUE      _isShowingOverlay: true

VideoScreen mouseRollOverOutListener COUNT: 6      (Odd numbers should always be MouseEvent type='rollOver')
[MouseEvent type="rollOver" bubbles=false cancelable=false eventPhase=2 localX=8 localY=78 stageX=8 stageY=78 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
Hiding overlay (expecting TRUE)...      _isShowingOverlay current value: true
Overlay should now be FALSE      _isShowingOverlay: false
----------------------------------------------------------

Here is the VideoScreen; just place it in a project sub-folder called 'controls' and use it as MXML:

<controls:VideoScreen id="videoScreen" width="300" height="225" fixDistortion="true" />





package com.futurewebstudios.controls
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.media.Video;
	import flash.net.NetStream;
	
	import mx.core.UIComponent;
	import mx.events.FlexEvent;
 
	public class VideoScreen extends UIComponent
	{
		
    //--------------------------------------------------------------------------
    //  Private properties
    //--------------------------------------------------------------------------		
		private const DEFAULT_WIDTH:Number = 320;
		private const DEFAULT_HEIGHT:Number = 240;
		private var _video:Video;
		private var _videoIsDistorted:Boolean;
		private var _overlay:Sprite;
		private var _isShowingOverlay:Boolean;
		private var _overlayShowCount:uint = 1;
		private var _stream:NetStream;
		private var _streamIsChanged:Boolean;  //changes to true once set, to false after used
		private var _maintainAspectRatio:Boolean; //needs no boolean state value; handled on property change
		
    //--------------------------------------------------------------------------
    //  Public properties
    //--------------------------------------------------------------------------
		[Bindable( event="videoIsDistortedChanged" )]
		[Inspectable( defaultValue="false" )]
		public function set fixDistortion( value:Boolean ):void
		{
			if ( _videoIsDistorted != value )
			{
				_videoIsDistorted = value;
			}
		}
		public function get fixDistortion():Boolean
		{
			return _videoIsDistorted;
		}
		
		[Bindable( event="streamChanged" )]
		[Inspectable( category="General", defaultValue="null" )]
		public function set stream( value:NetStream ):void
		{
	        if ( _stream != value )
	        {
	        	_stream = value;
	        	_streamIsChanged = true;
	         	dispatchEvent( new Event( "streamChanged" ) );   
	        }
		}
		public function get stream():NetStream
		{
			return _stream;
		}
		
		[Bindable( "maintainAspectRatioChanged" )]
		[Inspectable( defaultValue="true" )]
		public function set maintainAspectRatio( value:Boolean ):void
		{
			if ( _maintainAspectRatio != value )
			{
				_maintainAspectRatio = value;
				invalidateSize();
				invalidateDisplayList();
				dispatchEvent( new Event( "maintainAspectRatioChanged" ) );
			}
		}
		public function get maintainAspectRatio():Boolean
		{
			return _maintainAspectRatio;
		}
		
		 public function get  isShowingOverlay():Boolean
		 {
		 	return _isShowingOverlay;
		 }
	    
    //--------------------------------------------------------------------------
    //  Constructor
    //--------------------------------------------------------------------------
		public function VideoScreen()
		{
			this.addEventListener( FlexEvent.INITIALIZE, initializeListener );
			this.addEventListener( MouseEvent.ROLL_OVER, mouseRollOverOutListener );
			this.addEventListener( MouseEvent.ROLL_OUT, mouseRollOverOutListener );
			
			_video = new Video( this.DEFAULT_WIDTH, this.DEFAULT_HEIGHT );
			this.addChild( _video );
			
			super();
		}
		
    //--------------------------------------------------------------------------
    //  Public methods
    //--------------------------------------------------------------------------
	    public function attachNetStream( value:NetStream ):void
	    {
	    	_video.attachNetStream( value );
	    }
		public function showOverlay():void
		{
			if ( _isShowingOverlay == false )
			{
				trace( "Showing overlay (expecting FALSE)...\t_isShowingOverlay current value: " + _isShowingOverlay );
				_isShowingOverlay = true;
				addChild( _overlay );
				trace( "Overlay should now be TRUE\t\t\t_isShowingOverlay: " + _isShowingOverlay );
			}
			else
			{
				trace( "ERROR!!  _isShowingOverlay returns TRUE from showOverlay()" );
			}
		}
		public function hideOverlay():void
		{
			if ( _isShowingOverlay == true )
			{
				trace( "Hiding overlay (expecting TRUE)...\t\t_isShowingOverlay current value: " + _isShowingOverlay );
				_isShowingOverlay = false;
				removeChild( _overlay );
				trace( "Overlay should now be FALSE\t\t\t_isShowingOverlay: " + _isShowingOverlay );
			}
			else
			{
				trace( "ERROR!!  _isShowingOverlay returns FALSE from hideOverlay()" );
			}			
		}
	    
    //--------------------------------------------------------------------------
    //  Protected methods
    //--------------------------------------------------------------------------
		protected function setPublicValues():void
		{
			_video.width = this.width;
			_video.height = this.height;
			if ( _videoIsDistorted )
			{
				_video.smoothing = true;
			}
		}
		protected function createOverlay():void
		{
			_overlay = new Sprite();
			_overlay.graphics.beginFill( 0x000000, .5 );
			_overlay.graphics.drawRect( 0, 0,  this.width, this.height );
			_overlay.graphics.endFill();
			
			_isShowingOverlay = false;
			trace( "createOverlay() done.  Initial value should be false.  VALUE: " + _isShowingOverlay );
		}
		
    //--------------------------------------------------------------------------
    //  Private methods
    //--------------------------------------------------------------------------
		private function initializeListener( e:FlexEvent ):void
		{
			setPublicValues();
			createOverlay();
		}
		private function mouseRollOverOutListener( e:MouseEvent ):void
		{
			trace( "\nVideoScreen mouseRollOverOutListener COUNT: " + ( _overlayShowCount++ ) + "\t\t(Odd numbers should always be MouseEvent type='rollOver')\n" + e.toString() );
			( _isShowingOverlay == false ) ? showOverlay() : hideOverlay();
		}
	}
}

Open in new window

JaeWebbAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Gary BenadeCommented:
When you add the overlay as a child it effectively hijacks mouse events to the video control because it's on top of it, one way to fix this is to add the same event listeners to overlay as the video like below, another is to set overlay.mouseEnabled = false;


protected function createOverlay():void
                {
                        _overlay = new Sprite();
                        _overlay.graphics.beginFill( 0x000000, .5 );
                        _overlay.graphics.drawRect( 0, 0,  this.width, this.height );
                        _overlay.graphics.endFill();
                        
                        _isShowingOverlay = false;
                        trace( "createOverlay() done.  Initial value should be false.  VALUE: " + _isShowingOverlay );
 
                        overlay.addEventListener( MouseEvent.ROLL_OVER, mouseRollOverOutListener );
                        overlay.addEventListener( MouseEvent.ROLL_OUT, mouseRollOverOutListener );
 
                }

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
JaeWebbAuthor Commented:
If you notice the constructor, the component itself is the object listening for the events.  The video player is the first child, the overlay is the second child.  The target and current target of the MouseEvent.ROLL_OVER/OUT are always the VideoScreen component itself, never its children.  ROLL_OVER/OUT events do not bubble either.

When I follow your first suggestion and add event listeners to the overlays, the mouseRollOverOutListener is fired a bunch of times.  Just two quick swipes over the Screen produces this:

-- VideoScreen mouseRollOverOutListener --
COUNT: 89, EVENT TYPE: rollOver            (Odd numbers should always be MouseEvent type='rollOver')
Showing overlay (expecting FALSE)...      _isShowingOverlay current value: false
Overlay should now be TRUE                  _isShowingOverlay: true

-- VideoScreen mouseRollOverOutListener --
COUNT: 90, EVENT TYPE: rollOver            (Odd numbers should always be MouseEvent type='rollOver')
Hiding overlay (expecting TRUE)...            _isShowingOverlay current value: true
Overlay should now be FALSE                  _isShowingOverlay: false


When I remove the listeners at the component level and keep them on the overlays, the listener is never triggered.  When I listen for ROLL_OVER at the component level and OUT at the overlay level, I get crazy results - same thing reversed.  When I listen for ROLL_OVER on the video and ROLL_OUT on the overlay, I'm I don't get the results I expect.


When I tried your second idea and set mouseEnabled on the overlay to false, it worked as expected!  Your solution worked.   Thanks a lot.  I don't understand it since the listeners were at the container level and not at the children level, but hey - it works.

I am a bit worried about turning off the mouseEnabled property on the overlay though.  I'd like to emulate the functionality on this player:

http://www.huffingtonpost.com/2008/10/25/obama-mccain-target-the-w_n_137781.html

As you can see, when you mouseover the overlay close to the button, the button grows in response... That button must not be a child of the overlay as I originally assumed it was.  Any thoughts?  

Thanks again.


0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Adobe Flash

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.