Don’t forget the down state
I have been immersed in Flex 4 lately and had this issue come up. It seems like it might be fairly easy to run into and isn’t entirely obvious at first so I’ll share. I had a custom component that included buttons that weren’t registering click events.
Here’s the scenario. I created a custom component that contained a background rectangle for highlighting, a label to display the name of the item, a toggle button for expanding the item and a button for editing the item. I won’t get into the specifics of what this component is for, but just stick to the high level concept.
[ [expandBtn:ToggleButton] [labelDisplay:Label] [editBtn:Button] ]
The component had a number of states:
[SkinState("up")] [SkinState("over")] [SkinState("down")] [SkinState("upAndSelected")] [SkinState("overAndSelected")] [SkinState("downAndSelected")]
This is similar to the ToggleButton component in the SDK but didn’t need the disabled state. I created mouseEvent handlers to take the rollOver, rollOut, mouseDown, mouseUp and click events so I could handle them appropriately, setting properties for the getCurrentSkinState() method to use and setting invalidateSkinState() after changing the properties.
In the components up state, I wanted just the label to show and then when the user hovers over the component, the expand and edit buttons show. Using states, this is simple enough to accomplish in the skin for the component:
<s:states> <s:State name="up" /> <s:State name="over" stateGroups="overStates" /> <s:State name="down" stateGroups="downStates" /> <s:State name="upAndSelected" stateGroups="selectedStates" /> <s:State name="overAndSelected" stateGroups="overStates, selectedStates" /> <s:State name="downAndSelected" stateGroups="downStates, selectedStates" /> </s:states> ...<s:ToggleButton id="expandBtn" includeIn="overStates" .../><s:Label id="labelDisplay" ... /><s:Button id="editBtn" includeIn="overStates" .../>
The ellipses indicate additional code that is irrelevant.
I added the click handlers for the buttons in the custom component so I could capture the clicks and dispatch my own custom event.
So this starts out looking fine. The buttons show up when the mouse is hovered. Great. I click on the edit button and… nothing. The click handler is never called. Do you see why?
Hint: Because events like clicks travel the displayList, the surrounding component will react to the clicks of the buttons inside it too.
Ok, here’s the chain of events:
- The user presses the mouse button down over the edit button.
- The edit button gets the mouseDown and enters it’s down state (if it has one).
- The surrounding component gets the mouseDown and changes it’s skin state to the down state.
- The edit button is not included in the down state, so it is removed.
- The user releases the mouse button.
- The surrounding component gets the mouseUp and returns to the over state.
- The edit button is included in the over state so it appears again.
Since the edit button wasn’t included in the down state, it never had a chance to react to a click event. Click events occur when the user presses and releases the mouse button over a component and the edit button was not around for that whole chain of events.
Solution: Add the buttons into the downStates so they exist during the entire click action. Problem solved.