To say it is a long time coming is an understatement, but I found myself last week with a little time that I could devote to updating my Flash Equipment Interface component set. I didn’t quite get to them all, but I hit the ones I thought were the most important. Over the next few days, I’m going to clean things up and release them somewhere. I am not completely decided yet where/how I will distribute them. When I do that, if you have some need for one or more of the remaining components, let me know and I’ll do my best to create them.
Update: I decided to release the components as open source under the New BSD License. Check out my Google Code project to get them.
First off, I had made AS3 components over the years, here and there, and Jeff Kamerer’s multi-part series is really good (though I seem to discover new things each time I read it!). Here are few more I found useful to help fill in some details, or get other perspectives:
- ActionScript 3 Custom Components from Hell (Red Bjarne)
- Building an AS3 FLA based component with SWFPanel and LivePreview (FlashGrind)
- Creating Flash Components for Distribution (Andre Cavallari)
- Creating FLA-based Components with ActionScript 3 in Flash CS3 (Flexible Factory)
- Custom Component UI with ActionScript 3.0 (Jeff Kamerer)
In general, it was a somewhat benign (yet tedious) experience, but there were a few gotcha’s that I wanted to document.
The first question I asked myself was whether I should follow the pattern of the standard static constant-based events that are in the AS3 event model, for example, MouseEvent.MOUSE_DOWN. I realized that at the top of my priorities was integrating the interface components with my State Engine, and the constant-based approach is not directly compatible with that. For example, if all buttons issue MouseEvent.CLICK, and a state wants to listen to a specific click on a button, it means the state can really only subscribe to one button — without the handler having a mess to distinguish which button sent the event. I prefer the ability to allow the instance to customize the event message. Therefore, in my implementation, two buttons can send different events, such as “onPower” and “onNextPage”. In reality, this gives developers the option to use the default event name, or rename it for their purpose. I realize one of the complaints of the AS3 event model is that event handler typing is not enforced at compile time, and my approach flies in the face of that direction, but I didn’t see a compatible alternative.
Most of my work is done within Flash Professional, FlashDevelop, or FDT. I have not used CSS much at all, and so I decided for the first go around to make skinning something easy to accomplish with Flash Pro. I added a property that gives the class name for the skin, using the default skin class name as the default property value. That way, anyone who wants to create a skin just needs to make a Sprite or MovieClip (depending on the component) and give it a class name, then stick in the class name into the appropriate property. I need to work on documenting how the skin should be created (rotation base, size, etc.) but I hope it is straightforward. I played around a bit with setStyle() and the like, but in the end I went with what was easiest.
The most natural way of writing documentation is using ASDoc, but I struggled quite a lot getting it to work with my *.as files. It was one of those things that I thought would take 30 minutes that turned into more than half a day affair. One big discovery was hearing that I needed to use JRE 5, instead of the more recent JRE 6 or JRE 7 versions. I am shocked that this could have been the culprit, as Adobe seems to depend heavily on ASDoc and I wonder what the dependency is to version 5. I am surprised I don’t see it somewhere in Flash CS5, but then again, maybe not so surprised, as Adobe is pushing development through Flash Builder. This article from Wasted Potential helped a lot, though I still had some tweaks (hard earned through several hours). BTW, here is my batch file that worked:
cls set path=C:\Program Files (x86)\Adobe\Adobe Flash Builder 4.5\sdks\4.5.1\bin set JAVA_HOME="C:\Program Files (x86)\Java\jre1.5.0_22" asdoc.exe -source-path "C:\Users\Jonathan\Documents\My Dropbox\FMXIS3" -doc-sources "C:\Users\Jonathan\Documents\My Dropbox\FMXIS3\com" -window-title "EqSim Hierarchical State Engine and Equipment Interface Components" -main-title "Flash State Engine and Components" -footer "Copyright 2011<br/>www.eqsim.com" -output "C:\Users\Jonathan\Documents\My Dropbox\FMXIS3\docs" -library-path "C:\Users\Jonathan\Documents\My Dropbox\FMXIS3\fl-shim.swc" pause
You’ll see I set the path variable at top because I didn’t want to interfere with the real JAVA_HOME (and get inundated with Java update messages, just for ASDoc). Before having the library-path argument, I remember I had a lot of trouble with weird errors like UIComponent not being defined. That eventually led me to a site that discussed creating a shim with the UIComponent code, which you can see here. That site basically said to create a MovieClip that includes any Flash component (from Adobe), then export that clip as a SWC, then add the library-path argument above. With the benefit of foresight, I bet I could just drop a component on the stage, then go find the ComponentShim component in the _private folder that gets added when one brings in one of those components (I used a Button), and use that, but I’m too lazy to go back and try that.
I struggled with the decision about whether I would produce FLA-based components or SWC-based components. I have not come down conclusively on either side, yet. The easiest process seems to be letting Flash produce the file as part of an exported SWC, then using that SWF file in the Components panel under Live Preview. That wasn’t so helpful for two of my components, Timer and Stopwatch, because they have no visible presence at run-time, so the Live Preview would empty. I tried to follow Jeff Kamerer’s suggestions in his Part 3 on LivePreview, but I found he glossed over the most important part, unfortunately:
If I had wanted the UILoader Live Preview to handle component parameters, I would have simply implemented those parameters.
He doesn’t say how to implement the parameters, and his example doesn’t show how to retrieve a UILoader-specific parameter — the live preview movie itself is not of the type of the component, so I don’t know how to access the parameters I want to display. For example, in my Timer component, I want to show the timing interval. I don’t know how to access the timing interval of the real instance from the Live Preview movie. Very frustrating. Maybe someday I’ll figure it out, but for now I just created a box that shows up if isLivePreview is true (only during Live Preview). So I will deploy my Timer and Stopwatch as SWC components, and then using the default preview mechanism will show the box I wanted. Kind of gnawing at me how to access the parameters from a Live Preview movie, though.
UPDATE: I had some insight into the LivePreview, after giving it some more thought, but still did not get it to work. Here is my comment I added to Jeff Kamerer’s article on making a LivePreview:
This series has been great, but I have one problem for the life of me I can’t overcome (and this part, #3, does not give the details). I also have searched over the Internet at other tutorials, but to no avail.
I want to create a LivePreview movie for my component that has no visual representation on the stage. The UILoader example is good, but it leaves out a few critical details. For example, it says if you need to get component parameters, define them. However, it does not say how to reach the parameters in the instance. I looked in fl.livepreview.LivePreviewParent to find the answer: myInstance. That property is the component instance. Great, I’m thinking, almost there!
So inside the class I define for my LivePreview, for my component’s property “instance”, I retrieve “LivePreviewParent(parent).myInstance.interval” (because parent inherits from the LivePreviewParent class). If “interval” is defined as a public variable (with Inspectable), great, I get the value as I want. However, my “interval” happens to be a getter/setter (with an Inspectable tag, of course), and nothing happens. I’m stuck. I don’t know why it works for a public variable but not for a getter/setter.
Furthermore, even with an Inspectable variable, when I update the value in the property inspector, it does not change in the LivePreview. I see in the LivePreviewParent class there is a method called “onUpdate”. If I define that in my LivePreview movie, I’m thinking I will get notified when the properties change. Nope. I even tried defining it in the parent, and then having a function call into the LivePreview clip, but still nothing.
I have spent many hours on this, and just can’t afford to continue, so I have to go back to my fallback, which is adding the live preview into the real component and using the basic process. This inflates the size of my component, but it is the only thing I can see reliably working.