Archive for April, 2009

Just recently got a question from someone on how to work around the fact that you cannot create custom component UI for Flash Authoring components with AS3. We are all enjoying this new world of AS3, so what a pain to have to create custom comonent UI, even for your AS3 components, using AS2!

Now it is very reasonable to assume that it isn’t possible to create custom component UI with AS3, because there is no documentation and no article, no nothing, saying how to do this. And why would we go to all the effort to code and test a feature and not TELL anyone about it. We wouldn’t do that, right?

Well, we did that.

Note that this stuff doesn’t work in Flash CS3, but it will work in CS4. So if someone tries to use a component using an AS3 custom UI, the UI will show up in CS3 but it won’t actually work. So that is a pain.

OK, let’s back up. What is a custom component UI. This ancient article explains the very basic idea of it. Basically it is a SWF that shows up in the component inspector instead of the list of name/value pairs. You connect it to your component via the Component Definition dialog, which I’m sure you are already familiar with if you create components.

I made a sample AS3 custom component UI and you can download the zip. In the zip, you’ll find

  • CustomUIAS3.fla and .as, the source for the custom UI.
  • CustomUIAS3TestComponent.fla and .as, the source for the component that uses the custom UI.
  • ComponentTest.fla, a test file with the final published component on stage and the custom UI working.

You can immediately open ComponentTest.fla and play around with the interface. It looks like this:

Custom User Interface in AS3

Mine is a little ugly in that it requires you to click the Update button at the bottom for the version on stage to be updated. However, there is no reason that a real UI needs to do this. It can use all the events available in AS3 to be clever about when to update the instance on stage.

The basics are pretty basic, and I think definitely cleaner and nicer than the old AS3 xch way. Instead, it uses ExternalInterface.

To get your custom UI updating based on selection in authoring, you need to implement a public function onUpdate(...updateArray:Array):Boolean and expose it via ExternalInterface. You can either do this by making connecting a custom class to you main timline (which is what i did) or you can put the functions in a frame script on your main timeline. This function should always return true.

The updateArray that is passed in is an alternating list of name/value pairs. So for example if your component has two properties, label:String and num:Number, you might get the array: [ "label", "asdf", "num" 0 ]. You just need to step through this array and update your UI as appropriate.

Passing the updated information back uses an array formatted the same way. In fact in my example, I found it useful to stash the array passed into onUpdate(), update that array and then pass it back. The way you call back into Flash with your update is by calling ExternalInterface.call("updateProperties", propertiesArray).

You can even support complex types this way. In my example, I set a dataProvider property on a ComboBox component. With a collection type like this, instead, of getting a simple value in the array, you get an Object with a collectionArray:Array on it. I think my example spells out how to work with it pretty well.

I’ll leave you with the code for the CustomUIAS3 class. Hope you found this helpful!


package {
	import fl.controls.*;
	import fl.events.*;

	import flash.display.*;
	import flash.events.*;
	import flash.external.*;

	public class CustomUIAS3 extends Sprite {

		public var label_txt:TextInput;
		public var toggle:CheckBox;
		public var color:ColorPicker;
		public var dataGrid:DataGrid;
		public var num_txt:TextInput;
		public var plus_btn:Button;
		public var minus_btn:Button;
		public var submit_btn:Button;

		private var propertiesArray:Array;

		public function CustomUIAS3()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;

			if (ExternalInterface.available) {
				ExternalInterface.addCallback("onUpdate", onUpdate);

				submit_btn.addEventListener(MouseEvent.CLICK, updateAuthoring);
			}

			plus_btn.addEventListener(MouseEvent.CLICK, doPlus);
			minus_btn.addEventListener(MouseEvent.CLICK, doMinus);
		}

		private function doPlus(e:Event):void {
			dataGrid.dataProvider.addItem({label: "label", data: "data"});
		}

		private function doMinus(e:Event):void {
			dataGrid.dataProvider.removeItemAt(dataGrid.selectedIndex);
		}

		public function onUpdate(...updateArray:Array):Boolean
		{
			propertiesArray = updateArray;

			for (var i:int = 0; i + 1 < propertiesArray.length; i += 2) {
				var name:String = String(propertiesArray[i]);
				var value:* = propertiesArray[i+1];
				switch (name) {
				case "label":
					label_txt.text = value;
					break;
				case "toggle":
					toggle.selected = value;
					break;
				case "color":
					color.selectedColor = value;
					break;
				case "num":
					num_txt.text = String(value);
					break;
				case "dataProvider":
					var collectionArray:Array = value.collectionArray;
					var dp:DataProvider = dataGrid.dataProvider;
					dp.removeAll();
					for (var j:int = 0; j < collectionArray.length; j++) {
						var item:Object = collectionArray[j];
						dp.addItem({label: item.label, data: item.data });
					}
					break;
				}
			}

			return true;
		}

		private function updateAuthoring(e:Event):void
		{
			for (var i:int = 0; i + 1 < propertiesArray.length; i += 2) {
				var name:String = String(propertiesArray[i]);
				switch (name) {
				case "label":
					propertiesArray[i+1] = label_txt.text;
					break;
				case "toggle":
					propertiesArray[i+1] = toggle.selected;
					break;
				case "color":
					propertiesArray[i+1] = color.selectedColor;
					break;
				case "num":
					propertiesArray[i+1] = Number(num_txt.text);
					break;
				case "dataProvider":
					var propObj:Object = propertiesArray[i+1];
					var collectionArray:Array = new Array();
					var dp:DataProvider = dataGrid.dataProvider;
					for (var j:int = 0; j < dp.length; j++) {
						var item:Object = dp.getItemAt(j);
						collectionArray.push({label: item.label, data: item.data});
					}
					propObj.collectionArray = collectionArray;
					break;
				}
			}

			ExternalInterface.call("updateProperties", propertiesArray);
		}

	}

}

I wrote this article ages ago, and it was originally had “for CS3” in the title I think, but they took it out when I told them it didn’t really need to be updated for CS3. It has oodles of information in it about the User Interface AS3 components included with Flash Authoring.

http://www.adobe.com/devnet/flash/articles/creating_as3_components.html

So this is hardly like hot off the press news, but this article is useful and I fully expect I will reference it in my posts so that I can avoid typing all that stuff again.

I’ve been meaning to do this for ever, and I’m finally actually starting my blog. My idea is this blog is going to focus on some Flash things that I know a thing or two about, like ActionScript 3.0 and Flash Authoring Components. My idea is that I will be putting out useful information like tips, known bugs with work arounds, code snippets accompish cool stuff, etc.

But I should introduce myself, first. I’m Jeff Kamerer, and I’ve been on the Flash Authoring team since October 2002. I’ve worked on many features, including the FLVPlayback component (both AS2 and AS3 versions), support work for the AS3 UI components, ActionScript 3.0 support and Flex integration.

Over time I have continually found myself answering questions on mail threads, either internally at Adobe or on beta lists or whatever, and thought, “Wow, I bet a lot of people out there in the world would find this stuff useful! I should write an article.” Well, I have written some articles, but generally I find that pretty time consuming, so I realized if I am ever to get this information out into the world, a blog would be the best way.

So here it is, my blog! How exciting.