Wednesday, May 21, 2008

localToGlobal vs. contentToGlobal in Flex

If you hadn't heard, there are a set of functions in Flex called contentToGlobal and globalToContent that Sean Christmann and I discovered this afternoon doing some hacking.  If you're ever using globalToLocal or localToGlobal, you probably should be using the analagous "content" versions instead.  Here's why.

Let's say your application consists of two red boxes.  One of them is on your root or Application level stage, and the other is nested in a component who's x position is set to 5.  The Application level box is placed at x=10.  Let's say we want to make it so that both red boxes are at the same X position.  Since the box that contains the child is already 5 pixels from the left, we set our second red box's x to 5, combining for a total of 10.

Rather than doing these calculations by hand, Flex has a set of helpful methods that make it easy.  Traditionally I would do this using the "globalToLocal" and "localToGlobal" methods.  To calculate the X value using these methods we'd traditionally do something like this inside of our child container:

//get the application level red box's x coordinate
var box1_x : int = Application.application.box1.x;

//convert this x value into a global coordinate
var pt : Point = new Point(box1_x, 0);
var box1_global_x : int = Application.application.localToGlobal(pt).x;
//note: since box 1 is on the "global" stage, localToGlobal shouldn't do anything.
//I've just included it here for completeness.

//convert the global coordinate into our container's coordinate system
var pt2: Point = new Point(box1_global_x, 0);
var final_value : int = this.globalToLocal(pt2).x;

These five steps convert the box's x value into a global point and then back from global into our container's local coordinate system.  Setting box 2's x value to our "final_value" gives the desired result, as shown above.

The problems start showing up when your child's container uses a border.  Let's say we give our container a border thickness of 2.  If we run the code above to set our box's x position, we wind up with something like this:

Our second box renders displaced by a number of pixels equal to the border thickness from where we want it.

This is because Flex containers place their children inside a nested container called a "contentPane."  Content panes are separate containers that have their own coordinate systems - think of it as a box within a box.  In the outer box the component renders borders.  All children and their content are renderered in the inner box - the content box.  globalToLocal and localToGlobal calculations use the container's root coordinate system (the "outer box").  Since children are rendered inside of the content pane, globalToLocal type calculations really aren't "local" to these children.

The answer is to use contentToGlobal and globalToContent instead.  Replacing localToGlobal and globalToLocal in our previous example, the red box renders correctly, like this:

If you don't have any borders or padding then the two boxes line up and localToGlobal / globalToLocal works just fine, but if you do have them, these methods will fail you because they're based on the "outer box" coordinate system and not the content pane.

Friday, May 16, 2008

Intern Inside

We've got an intern starting monday.  I made this sweet logo for his laptop and thought I'd share with all the other great geek interns out there.

Wednesday, May 14, 2008

Flash Player 10 released

With Ryan Stewart hinting on Twitter about exciting news to come from Adobe tonight, I stayed up for a while and was rewarded to be one of the first to learn about the release of Flash Player 10.  I gave a full synopsis of it on InsideRIA so I won't repeat that here - check it out!

I can't get the PixelBender to work in Safari - anyone else have any luck?