Email Updates RSS Subscribe
Line

This blog is created and maintained by the technical team at Hook in an effort to preserve and share the insights and experience gained during the research and testing phases of our development process. Often, much of this information is lost or hidden once a project is completed. These articles aim to revisit, expand and/or review the concepts that seem worth exploring further. The site also serves as a platform for releasing tools developed internally to help streamline ad development.

Launch
Line

Hook is a digital production company that develops interactive content for industry leading agencies and their brands. For more information visit www.byhook.com.

Line

Utility – Outliner (Flash Extension Panel)

Line
Posted on December 6th, 2009 by Jake
Line

Let me guess, it goes a bit like this:

“Click + Drag, crap grabbed the wrong one, slower click, still the wrong item, lock, lock, lock, lock, click, finally. Double Click, crap.. locked..unlock, unlock, unlock, double click, f@&^k!”

Managing the seemingly simple act of selecting objects on stage quickly becomes a nightmare for any flash designer. The solution? JSFL! (or so we thought)

This is the tale of an Outliner style utility that helps manage the normally arduous process of selecting the right objects.

Download Hook Outliner Here

Download Outliner

So we set out to build something with similar features to Autodesk Maya’s Outliner. This would allow a simplified selection mechanism and a bit more organized way of viewing things. The wish list ended up being something like this:

  • Simplified selection/deselection
  • Object information
  • Object search
  • Locking controls
  • Visibility controls
  • Better Library Access

In this posted version we managed to get the really “required” features in, but we still have more to go. However we wanted to get this out there as it has already eased some of the pain around the office. So we decided to roll it out in its current beta state.

JSFL: The Forgotten Road

The journey leading into trying to build a flash extension seemed so very bright and full of optimism, as if we were headed into a gingerbread town with roads paved using delicious candies. The possibilities were limitless, or so it seemed.

As with most things we started out doing a bit of research on how to build a panel and get it to interact with items on the stage. This is when the first wisps of oddity showed up. There seemed to be very little written on JSFL after flash 8. In all honesty there didn’t seem to be much written at all even for versions of flash before 8. These candy roads seemed a bit lonely, but hey, its still candy right?

After a bit of fumbling through the “Extending Flash” section of the docs, we managed to get a panel open with buttons and everything! However the buttons didn’t do much. As it turns out the link between the panel (a swf) and the flash IDE is a bit flimsy. Basically you call javascript formatted functions that feed results to the IDE. This on the surface sounds just fine, until you realize that any results you get from functions written in JSFL thare are returned to the panel are returned as pure strings. Meaning no real object references and no direct control. Suddenly the amazing candy roads were revealed as being paved with crappy powder dusted Necco Wafers.

So, how to click on an object in the panel, and have it actually select in the IDE. Our solution was two fold.

//Instance only props
if(curObj.elementType == "instance")
{//instance
	returnString += ("<prop propName=\"libraryName\" value=\"" + curObj.libraryItem.name + "\"/>");
 
	//check for persitant data
	if(curObj.hasPersistentData("uniqueID"))
	{//use old data
		returnString += ("<prop propName=\"uniqueID\" value=\"" + curObj.getPersistentData("uniqueID") + "\"/>");
	}//use old data
	else
	{//make new
		curObj.setPersistentData("uniqueID", "string", createUUID());
		returnString += ("<prop propName=\"uniqueID\" value=\"" + curObj.getPersistentData("uniqueID") + "\"/>");
	}//make new
}//instance
else
{//not an instance
	returnString += ("<prop propName=\"libraryName\" value=\"null\"/>");
	returnString += ("<prop propName=\"uniqueID\" value=\"null\"/>");
}//not an instance

The first solution was to use an odd method for IDE elements of type “instance” called setPersistentData(). This method will allow you to store some data with an object in the .fla that is saved with the .fla. Soooo, that means we can make our own ID for the object, and pass that ID back to the panel. When something is selected in the panel, our custom ID is passed to the JSFL function, and we can then search through the DOM to find our object. Be aware that every time you have an object on a different keyframe in the IDE, that is technically a new instance, which means it will get its own ID, so there is no continuity there.

var propObj = {};
propObj["x"] = curObj["x"];
propObj["y"] = curObj["y"];
propObj["scaleX"] = curObj["scaleX"];
propObj["scaleY"] = curObj["scaleY"];
propObj["rotation"] = curObj["rotation"];
propObj["Layer"] = curObj["layer"];
 
//loop through objects and match all recorded properties
	for(var i = 0; i < objList.length; i++)
	{//check each object
		var curObj = objList[i];
 
		for (var prop in propObj)
		{//check each prop desc object
			var propsMatch = true;
			//var propObj = propListObj[j];
 
			if(prop == "Layer")
			{//layer
				if(propObj[prop] != curObj['layer']['name'])
				{//no match
					propsMatch = false;
					break;
				}//no match
 
			}//layer
			else if(propObj[prop] != curObj[prop])
			{//no match.. fail
				propsMatch = false;
				break;
			}//no match.. fail
 
		}//check each prop desc object
 
		if(propsMatch)
		{//found one
			selectionObj = curObj;
			return curObj;
		}//found one
 
	}//check each object

That first solution is fine unless you want to try to select a shape or something that isn’t a symbol or bitmap. So we needed a fallback method of selecting items that couldn’t have persistent data stored on them. The resulting second solution was to basically “guess” based on matching properties. So in true brute force style, we loop through each object in the IDE that we care about and compare its properties to a property object that is sent from the panel to JSFL. “But Wait!”, I hear you cry. “You said we can’t pass objects, only strings!”. That is still correct, so we build an object from a string:

eval('[{"name":"_testMC","id":"ADEC39E38EDB492F81DDD20836ABD7E5","type":"instance"}]');

Please note that is just an example, more properties are in that string than the 3 listed. And yes we know its awful, but it does in fact work.

So far we’ve sort of glossed over one very important problem, how to get information about an object from JSFL back to the panel. The answer we came up with was our old friend XML.

function serialize(list)
{//serialize
        var returnString = "<objects>";
 
	for(var i = 0; i < list.length; i++)
	{//list
		returnString += ("<object>");
		var curObj = list[i];
		for( var prop in curObj)
		{//each prop
			//fl.trace("Prop: " + prop);
			if(prop == "brightness" || prop == "tintColor" || prop == "tintPercent" || prop == "actionScript")
			{//clean
				returnString += ("<prop propName=\"" + prop + "\" value=\"" + ("null") + "\"/>");
			}//clean
			else
			{//normal prop
				returnString += ("<prop propName=\"" + prop + "\" value=\"" + (curObj[prop]) + "\"/>");
			}//normal prop
		}//each prop
 
 
		//Layer props
		returnString += ("<prop propName=\"layerLocked\" value=\"" + curObj.layer.locked + "\"/>");
		returnString += ("<prop propName=\"layerName\" value=\"" + curObj.layer.name + "\"/>");
 
		//Instance only props
		if(curObj.elementType == "instance")
		{//
			returnString += ("<prop propName=\"libraryName\" value=\"" + curObj.libraryItem.name + "\"/>");
 
			//check for persitant data
			if(curObj.hasPersistentData("uniqueID"))
			{//use old data
				returnString += ("<prop propName=\"uniqueID\" value=\"" + curObj.getPersistentData("uniqueID") + "\"/>");
			}//use old data
			else
			{//make new
				curObj.setPersistentData("uniqueID", "string", createUUID());
				returnString += ("<prop propName=\"uniqueID\" value=\"" + curObj.getPersistentData("uniqueID") + "\"/>");
			}//make new
		}//
		else
		{//not an instance
			returnString += ("<prop propName=\"libraryName\" value=\"null\"/>");
			returnString += ("<prop propName=\"uniqueID\" value=\"null\"/>");
		}//not an instance
 
		returnString += "</object>";
 
	}//list
 
	returnString += ("</objects>");
 
	//fl.trace("ObjString: " + returnString);
 
	return returnString;
}//serialize

What we do is walk through each property on the object and build one long XML string from it manually. Additionally we add any custom properties that we need such as the ID mentioned earlier. Its at this point that the persistent data is set and retrieved, if available.

Pitfalls, ATARI Style

This is a random list of issues that we have run into with JSFL thus far:

  • As far as we can tell, there are no real events to catch from the IDE. So updating the IDE or a panel seems to be up to the panel to poll the IDE… booooo…
  • It seems you have to force an update on the IDE after you change something from the JSFL.
  • There doesn’t seem to be an official way to force an IDE update, so we hacked it by setting and then immediately unsetting the .silent property on the current document.. boooo to that as well…

We have emerged from the depths, a little older, and a little jaded (mostly because of the flying monkeys), but fairly successful. So please let us know what you think of this panel and if its managed to help in your every day production!

Instructions

To install, save the .mxp file from the link below, and either double click on the .mxp or select File -> Install Extension in the Adobe Extension Manager. Finally restart Flash if you had it open during the install.

To run the panel, in the Flash IDE go to Window -> Other Panels -> HookOutliner

Download Hook Outliner

To use select your filter method on the left (Objects On Frame drop down list). This will populate the grid below with information about the objects from that filter. You can click on an object in that grid to select it, shift select for multiple.

The Select All and Select None buttons will select all things that are not locked in the grid, and the select none button will deselect everything.

The Auto Unlock On Pick will automatically unlock the layer and select whatever you select from the grid.

The Lock Other Layers button will lock all layers that are not selected. This can be useful to ensure that you are only manipulating the objects you want.

The Edit Clip button will take you into a selected instance.

The top row of buttons will change the size of the grid.

The Auto Refresh and Force Refresh buttons change how often the grid is updated. If Auto Refresh is checked, it will update the grid every half second or so. You can click the Force Refresh button to update the grid immediately.

This panel is still very beta and the known issues are as follows:

  • The blue row selection doesn’t always accurately reflect what items are currently selected.
  • Having Auto Refresh enabled on a complex fla might effect the performance of a running compiled swf. (For instance after you do a Test Movie)
  • Auto Refresh also seems to interfere with typing a value into a pen tool property on some machines.

If you find a bug, please let us know in the comments below. Thanks!

This product is licensed under GPLv3.

Line
2 Responses to “Utility – Outliner (Flash Extension Panel)”
  1. [...] seeing how some of this flash panel stuff is done, please check out our other posts on the subject: Hook Outliner and Hook [...]


Leave a Reply

*

Line
Line
Pony