Any photoshop gurus in the house will definitely see my PS noob-itude when you see the screenshots.
So, I get to the part where I have to take my glorious creation and make it real. Pshaw, I say! No problem. It's now just about a week later and I'm just about through the other side. I had to write a ton of components, and this is about one of them.
Here's part of what I wanted to do:

That's two Canvases with lots of stuff on them that come in from the top and the bottom of the screen and overlap each other. I didn't realize until I hit the wall what was wrong with that, and you may not either, so I'll tell you.
Whichever Canvas is on top gets all the events, whether it has something there or not. So in my case the bottom panel was last (topmost) in the display list, so its button worked great, but the right button didn't work at all.
I tried a few different things to get this to work, before I finally got something that did. I even in a moment of desperation tried to have a hidden canvas that found the controls in the intersection rect on both canvases and cloned them. Sadly, there's no generic clone method for UIComponents.
So, what I ended up doing was this - those two panels are in a "smart" container that manages their sliding up and down and changing sizes and what not, and that container subscribes to the mouse events..
private function addMouseListeners():void
{
addEventListener(MouseEvent.CLICK,handleIntersectingPanelMouseEvents);
addEventListener(MouseEvent.DOUBLE_CLICK,handleIntersectingPanelMouseEvents);
addEventListener(MouseEvent.MOUSE_MOVE,handleIntersectingPanelMouseEvents);
}
Passing the click events through turned out to be not too hard - when the effects finished playing (to adjust panel sizes or positions) - I recalculate the area of intersection of the two panels.. Then, when the mouse events fire, if I'm within that area of intersection, I clone the click and send it through:
var panel:AbstractPanelBase = getChildAt(0) as AbstractPanelBase;
for each (var uic:UIComponent in panel.getChildren())
{
if (uic.hitTestPoint(evt.stageX,evt.stageY))
{
// Hardcoded to button, to fix this make sure border objects and such are not in main children array
if (uic is Button)
{
newOver = uic as EventDispatcher;
if (evt.type == MouseEvent.MOUSE_MOVE)
{
// Moves have to be interpreted into ins and outs
if (overControl!=uic)
{
if (overControl!=null) // Mouse out and mouse over
{
var newEvt:MouseEvent = new MouseEvent(MouseEvent.ROLL_OUT,true,false,
evt.localX,evt.localY,evt.relatedObject,evt.ctrlKey,evt.altKey,
evt.shiftKey,evt.buttonDown,evt.delta);
overControl.dispatchEvent(newEvt);
}
// Fire the mouse over
overControl = uic as EventDispatcher;
newEvt = new MouseEvent(MouseEvent.ROLL_OVER,true,false,
evt.localX,evt.localY,evt.relatedObject,evt.ctrlKey,evt.altKey,
evt.shiftKey,evt.buttonDown,evt.delta);
overControl.dispatchEvent(newEvt);
}
} else
{
// Clicks just get sent through..
newEvt = evt.clone() as MouseEvent;
// Send only to topmost child, hopefully that is 0
(uic as EventDispatcher).dispatchEvent(newEvt);
}
}
}
}
That bit of code passes the clicks through, but it also does ROLL_OUT and ROLL_OVER events since just passing MouseMove events through won't do it. We're not handling a few more complex cases here, like:
- If the control we're worried about isn't a button. It's hardcoded like that because all I'm currently worried about are buttons, and there's some other stuff in the intersection area that would have to be accounted for otherwise. It would be easy enough to extend to a general case.
- We're not handling overlapped items inside the the panels - if there are two buttons overlapped the hit test will just find the first one and send the event to it.
So that's the basics of it. If I'd known what a pain it was going to be, I probably would have designed my UI a little differently, but I really don't regard time spent learning how to do difficult things in Flex as wasted time. I've done quite a few out there things in this language so far, and I expect to do quite a few more.
// There was a control that was registered as having the mouse over it,
// but this event didn't find one, so overControl is no longer valid and we need to fire a mouse out
if (overControl!=null && newOver==null)
{
newEvt = new MouseEvent(MouseEvent.ROLL_OUT,true,false,
evt.localX,evt.localY,evt.relatedObject,evt.ctrlKey,evt.altKey,
evt.shiftKey,evt.buttonDown,evt.delta);
overControl.dispatchEvent(newEvt);
overControl = null;
}
// stop propagation of the old event here
evt.stopImmediatePropagation()
Oh, one last VERY important thing that I of course, forgot:
You must remove all of the mouse listeners at the start of that event handler, and then put them all back as you leave it. It sucks, but otherwise you'll get a nice infinite loop. I experimented with mouseEnabled and mouseChildren and neither in any combination does it, unfortunately.
0 comments:
Post a Comment