Wednesday, February 25, 2009

Darren on Flex: Flexins, Drag 'n' Drop Mixin

I present to you the Drag'n'Drop Mixin

I've managed to embed an instance of my Drag'n'Drop showcase app here, but I cannot seem to influence the size of it. I recommend you visit the mixin's demo page here For those of you who are not interested in my posturing and story-telling, you can review the source for this application here at GitHub.

So how does it work?

You simply include <dnd:DnDMixin /> as a child of any container type and this enables that container as a drop target and any child as a drag source. It's as simple as that.

That's how you use it... How does it work?

Ok, here's an excerpt from the project's README file:
These mixins are designed to be used in MXML, leveraging components' event-driven life-cylce. They should be used as 'drop-ins', similar to usage model for inline item-renderers.
In addition to this, I exploit the Container.rawChildren property, due to the fact that this mixin is a content-less UI component and should not interfere with content-full UI components. When an application is loaded all declared UI components are processed through an event-driven life-cycle. The life-cycle events the dnd-mixin is interested in are:
FlexEvent.ADD:
Registration happens in the constructor, the earliest possible opportunity at the object level. More significantly, registration occurs before this (typically, one time) event is dispatched. The handler for this event deregisters from this same event, removes the dnd-mixin from the container and then adds it back but to the rawChildren. The handler then registers interest in the DragEvent.DRAG_ENTER, DragEvent.DRAG_OVER, DragEvent.DRAG_EXIT and DragEvent.DRAG_DROP events. The corresponding handlers implement the necessary mechanics to provide drag'n'drop functionality; I refer you to the source code as it's pretty straight forward and not that interesting, especially if you've implemented D'n'D yourself before. The handler also registers interest in the following events:
ChildExistenceChangedEvent.CHILD_ADD:
Through this event the dnd-mixin is notified whenever a component is added to the parent container. The addition of a child as far as the dnd-mixin is concerned, indicates a successful drag'n'drop operation to this parent container. Thus this event signals this dnd-mixin instance to take control of the D'n'D processing for this child component (to which a reference can be obtained from the event object). This handler registers interest in the following events on each managed child component:
MouseEvent.MOUSE_MOVE:
The registered handler simply detects the drag gesture (initiation) and delegates the operation to the DragManager.
DragEvent.DRAG_COMPLETE:
The registered handler basically just adds the dragged component to the new container, which implicitly removes it from its current/previous parent. Doing so dispatches the ChildExistenceChangedEvents, as explained here.
ChildExistenceChangedEvent.CHILD_REMOVE:
Similar to CHILD_ADD, this CHILD_REMOVE event notifies the dnd-mixin whenever a component is removed from the parent container. The removal of a child likewise indicates a successful drag'n'drop operation away from this parent container. Hence this event signals this dnd-mixin instance to relinquish control for D'n'D processing. This is explicit and is implemented through deregistering interest in the MouseEvent.MOUSE_MOVE and DragEvent.DRAG_COMPLETE events.
And that's all there is to it. Try it out above or here. As you'll see there is nothing spectacular about the end result; it's functional. The benefit is the improved readability in your code gained from the clean separation, reduced duplication and declarative style the mixins adopt. While I declare this dnd-mixin is fully functional I don't presume it will suit the needs of all drag'n'drop use cases. I have also observed a bug in the rendering of dragged items that were previously clipped by the parent's right edge. I think I recall a colleague encountering this problem (and solving it) so I'll ask her about it at the next available chance.

What's next?

Drag source filtering. This will support the use case if you want to allow some types of things to be dropped, but not others. The implementation for this is actually complete (see here); a blog will follow soon. If you want to use it, you know where the source/binaries are. Enjoy

No comments: