Recently I had the need for a component that would collapse and hide its contents, with some sort of toggle on the border. These sorts of components are common enough in the wild, and I thought I might find one on Flexlib, an excellent open source Flex component library. Unfortunately Flexlib didn't have what I needed, but it had something close. It had recently added a "WindowShade" component:
This example of WindowShade usage is a copy of an example Doug McCune posted on his blog http://dougmccune.com/blog/2008/02/09/windowshade-component-added-to-flexlib/.
I played around with it and saw that it had some of what I wanted, but it only went in one direction - vertical. The specific application I had in mind would be horizontal, and the arrow icons had to behave differently. What I really wanted was a little tab along the side of the screen with an arrow pointing out in the direction the box will expand if you click in - a general purpose way to show and hide entire portions of the screen.
But taking a look at the code I realized that the WindowShade was most of what I wanted. So I decided to try to adapt the WindowShade for my purposes, but hopefully in a general enough manner that other folks will find it useful as a general purpose container.
I started out by adding a "direction" property to the WindowShade. The default would be "vertical", and I added "horizontal". In horizontal mode the button will be located along the side of the container, and be taller than it is wide. This also necessitated adding two new values for "headerLocation" - "left", and "right".
But I ran into a bit of a problem, the direction property didn't specify enough information about the behavior of the component when it collapsed. Ideally I wanted the header arrow to indicate the direction the header or contents would move in when the header is clicked. If a component is anchored to the left side of its parent, that direction will be left - if that same component is anchored to the right side of its parent, that direction will be right. Clearly a simple vertical/horizontal choice for direction doesn't give me enough information to determine which arrow icons I should display in the header.
So I removed "direction" and added a "collapseDirection" property, which allows the user to tell the component what direction it collapses in, up, down, left, or right. This tells us both the horizontal/vertical layout, and gives us enough information to determine the icons to display in the header.
I then modified the layout code to resize the button and position it properly on either the left of right side of the container. It worked great. Until I added a label to the header. See, I was just resizing the header button to be taller than it was wide, but the button was still layed out normally, with a horizontal label. To do it properly, I really needed to rotate the button. I'd worked a bit with rotating skins on a previous component, so using that as an example, I added the following to the code that initializes the header button:
if (_direction=="horizontal") {
var m : Matrix = new Matrix();
m.rotate(Math.PI/2);
_headerButton.transform.matrix = m;
}
Pretty simple really. This snippet just rotates the button 90 degrees clockwise if the container has a horizontal direction. direction is still around as a local variable, but it is set automatically when collapseDirection is set.
The fun bit about rotation is that (0,0) rotates with the button, so I had to add some special logic to properly position rotated buttons in updateDisplayList().
And that about did it. I added some other properties that allow me to turn the header button into more of a tab, and position the tab along the edge of the container - these are headerWidth, and headerAlign respectively.
Here is an example:
I've also created a wrapper class called WindowShade, that initializes a CollapsibleBox with WindowShade like behaviors, but adds all of the other capabilities of the CollapsibleBox. It should be a drop in replacement for the original WindowShade component. The bottom drawer in the example has three horizontal WindowShade containers in a stack.
The source for the example is here: Download TestCollapsiblePanel.mxml (6.5K). I've integrated CollapsibleBox into my flex components library - available here: Download flexcomponents.swc (333.0K), eventually though I'd like to get some of these changes integrated back into Flexlib.
The full source code and asdoc for these classes, and all of my flex components is available here: Download flexcomponents.zip (146.5K)