Pages

Tuesday, November 30, 2010

Alternativ context menu for Flash, part 2

The first part showed you how to fetch the right click event and how to forward it to the Flash player. The events were process on the application level, which is not very useful for complex applications. Instead you want your sub components to process the right click event.

Therefore the following steps need to be done:
  • Find the component under the mouse cursor
  • Dispatch a right click event on this this component 
  • Listen to right click events dispatch from a component or its sub components (event bubbling)

The first step can be accomplished using the following code:

private function findComponentBelowCursor():UIComponent
{
  var globalPoint:Point = new Point( FlexGlobals.topLevelApplication.mouseX, FlexGlobals.topLevelApplication.mouseY );
  var objects:Array = FlexGlobals.topLevelApplication.getObjectsUnderPoint( globalPoint );
   
  for ( var i:int = objects.length - 1; i >= 0; i-- )
  {
    var o:Object = objects[ i ];
    if( o is UIComponent )
    {
      return o as UIComponent;
    }
    if( o.hasOwnProperty( "parent" ) && o.parent is UIComponent )
    {
      return o.parent as UIComponent;
    }
    if( o.hasOwnProperty( "document" ) && o.document is UIComponent )
    {
      return o.document as UIComponent;
    }
  }
  return null;
}

The function getObjectsUnderPoint returns all  DisplayObjects under the given point (globalPoint), which are searched for the topmost UIComponent.

If a UIComponent was found, a bubbling event is dispatched:

private function dispatchRightClickEvent( component:UIComponent ):void
{
  var globalPoint:Point = new Point( FlexGlobals.topLevelApplication.mouseX, FlexGlobals.topLevelApplication.mouseY );
  var localPoint:Point = object.globalToLocal( globalPoint );
  object.dispatchEvent( new RightClickEvent( RightClickEvent.RIGHT_CLICK, true, true, localPoint.x, localPoint.y, object ));
}


Note that a custom RightClickEvent extending the MouseEvent was used in the sample code. Also it is very important to set bubbling to true so that parent components can listen to the event as well.

Here is a small sample application:

To view this page ensure that Adobe Flash Player version 10.0.0 or greater is installed.



If you right click into one of the lists, the RightClickEvent is dispatched on DefaultItemRenderer, bubbles up to the list which has event listener registered and is processed in the event handler.

It is also very important to properly clean up the menu every time you click outside of the menu or open another one. For this you can use a static manager class like the following:

package
{
  import mx.controls.Menu;
  import mx.core.FlexGlobals;
  import mx.managers.PopUpManager;

  public class RightClickMenuManager
  {
    private static var menu:Menu;
  
    public static function showMenu( menu:Menu ):void
    {
      hideMenu();
      menu.show( FlexGlobals.topLevelApplication.mouseX, FlexGlobals.topLevelApplication.mouseY );
      RightClickMenuManager.menu = menu;
    }
  
    public static function hideMenu():void
    {
      if ( menu != null )
      {
        PopUpManager.removePopUp( menu );
      }
    }
  }
}

Monday, November 29, 2010

Alternativ context menu for Flash, part 1

The standard context menu of Flash imposes a few limitations which make it difficult to build a complex user interface with good usability. There are the following limitations:
  • standard menu items like "About Flash Player" cannot be removed
  • you can have 15 menu items at max
  • you can not have sub menus
  • several key words are reserved, like "copy" und "cut" and can not be used

Some clever developers have found a way to catch the right click event in the HTML container using Javascript and to forward the event to the Flash Player via the External interface (see here for the project site).

A right click in the following sample application opens a simple Flex menu which does not have the limitations descriped above.

To view this page ensure that Adobe Flash Player version 10.0.0 or greater is installed.




Now let's have a look at the code:

import mx.controls.Menu;
import mx.events.FlexMouseEvent;
import mx.managers.PopUpManager;

private var menu:Menu;

private function creationCompleteHandler( event:Event ):void
{
  ExternalInterface.addCallback( "rightClick", rightClickHandler );
}
private function rightClickHandler():void
{
  showMenu();
}
public function showMenu():void
{
  hideMenu();
  menu = Menu.createMenu( this, menuItems );
  menu.show( stage.mouseX, stage.mouseY );
}
public function hideMenu():void
{
  if( menu != null )
  {
    PopUpManager.removePopUp( menu );
  }
}

In the creationCompleteHandler  a callback is added to external interface which gets called every time the right mouse button is pressed.The callback rightClickHandler simply opens the Flex menu.

Check out the following links for more information on the topic: