Flex 2.0 Tree with Spring Loaded Folders
January 25, 2007
Although the techniques described here can be applied in other ways, and on other components; one of the most practical is the tree control.
When dragging items in a Flex application the user is mouse blocked. The left mouse button is used to initiate, and keep alive the drag operation. The tree control being comprised of folders and items posses a particular problem. If the user wishes to drag the items into a closed folder, the user would have to first open the desired destination folder, then perform the drag operation.
The Mac OSX has a really good implementation of the technique. I’m quite sure that it’s been around for some time. Here is a little blog post on the Mac version of this with a video. Apparently it was one of the most requested features users wanted back.
Though this implementation does not resemble the Mac OS version, they do share certain traits. I did ponder over the name “Spring Loaded Folders”, and in the end decided to stick with it. After all, it is kind of springy.
A Felx Spring Loaded Folder implementation:
When the user is dragging items, certain assumptions can be made about how to best help the user complete their task. By implementing a delayed timer, we can after a set period open the folder the user is hovering over allowing the user to complete their task.
However if the user opens a folder by hovering over it, it still may not be the folder they wish to drop the item into. As the user moves in and out of folders they are automatically opened or closed depending on certain criteria.
I store a copy of the open items before the drag start, so that the original state can be restored later. This original state is restored on drag exit and drag complete. I also compare the original open items during the close/open calls so that nodes triggered to open, will cross check with the original state in order to keep the original state intact.
Using the spacebar while dragging over a closed node will immediately open it, and cancel any timer.
I’ve added a opening indication. The opening indication will fade in and out the tree item renderer while the timer is running.
Here is a list of added properties.
Some of the difficulties I ran into:
The tree control is set up to allow only one opening or closing animation at a time. If we try to close/open multiple folders at the same time one blocks the other. However by using the ITEM_OPEN and ITEM_CLOSE tree events, and walking the close stack one at a time, I was able to set this up recursively.
The drag over event is not dispatched when you move into a node, rather into and around it. This causes problems as it’s dispatched many, many times even when moving over the same node. I tried my best to compare the current and last node so that the actions could be minimized, and only dispatch the delay open/close when the criteria is acceptable.
Closing a folder with animation when that folder has no children exposes a bug in flex. So if the node has no children I set the animate on the expandItem call to false. There was no visible way around this, and if it’s empty there was no real reason to show an animation anyway.
You’ll have a better experience when opening nodes moving in an upward direction. Moving downward displaces the dragging over location when the nodes above it close. So you may want to tweak it so that it only auto opens the nodes, and restores the state on exit and complete, as opposed to auto open and auto close.
I tried several different animation effects on the open indication and decided that fade was suitable (simple is better). This is very easy to change so have at it.
The delayed timer class included can be used in many places to achieve similar effects when dragging. Dragging data grid items between tab navigator tabs for example. I’m hoping that Michael Schmalle will think about this technique for his up coming MDI components.
The sample is using a XMLListCollection as the data provider for the tree. I haven’t tested any other provider types, and tried to limit the use of xml to only @isBranch.
With a couple of really simple modifications this should work with other data providers.
If you find good places to use this technique, your users will love you for it. Tweak, modify, and extend to your hearts content.
Ideas and feedback ?
Edit : A nasty framework bug was discovered and corrected. Sample and source have been updated. I’ve created a new post describing the bug. Turns out it’s a good one to remember.
Requires Flash 9.
Sample and Source: Spring Loaded Folders