Monday 28 November 2011

Custom Adobe Bridge Panel

A client recently requested a layout for a custom panel in Bridge which could not be done within the simple generic panel supplied by Adobe, and so had to be coded in Flash Builder.

After installing Flash Builder and the XMP Panel toolkit, it was time to start work!
Having not used flash / flex before, it was a bit of a learning curve, so this post attempts to describe what I have done, and how it works!

So the requirement was to have a grid of checkboxes on the panel, and as they were checked / unchecked they would update a text field which would be saved as part of the XMP metadata.

This field would then be read by ResourceSpace, which would associate those keywords in the field with categories and auto generate themes.

The keywords in the XMP field needed to be separated by a comma for this to happen.

So, the problem is twofold.
First, how to update the text field when the checkbox is ticked?
Second, how to tick the relevant checkboxes when the panel is loaded if there is text in the field?

So, I've laid out a grid in the panel, with checkboxes on it, which looks like this.
The text box underneath is the field that will contain the keywords.

The code for the grid is:
<mx:FormItem label="Categories" direction="horizontal" name="categories">
<mx:Grid name="mgrid" id="mygrid">
<mx:GridRow width="100%" height="100%">
<mx:GridItem width="100%" height="100%">
<mx:CheckBox label="Dockers"
id="_chkDockers"
name="_chkDockers"
click="updateCats(event)"/>
</mx:GridItem>
<mx:GridItem width="100%" height="100%">
<mx:CheckBox label="Trains"
id="_chkTrains"
click="updateCats(event)"/>
</mx:GridItem>
<mx:GridItem width="100%" height="100%">
<mx:CheckBox label="Aviation"
id="_chkAviation"
click="updateCats(event)"/>
</mx:GridItem>
<mx:GridItem width="100%" height="100%">
<mx:CheckBox label="Boat"
id="_chkBoat"
click="updateCats(event)"/>
</mx:GridItem>
<mx:GridItem width="100%" height="100%">
<mx:CheckBox label="Education"
id="_chkEducation"
name="_chkEducation"
click="updateCats(event)"/>
</mx:GridItem>
<mx:GridItem width="100%" height="100%">
<mx:CheckBox width="51
label="Road
click="updateCats(event)" 
id="_chkRoad"/>
</mx:GridItem>
</mx:GridRow>
</mx:Grid>
</mx:FormItem>


Each checkbox is contained within a grid item, and has an ID associated with it. It also calls a function updateCats() when the box is clicked, more about this later.

The code for the text field is thus:
<fi:XMPFormItem 
id="ftitle"
name="ftitle"
label="$$$/xmp/sdk/custompanels/Dave3/Title=Main Category:
labelTooltip="$$$/xmp/sdk/custompanels/Dave3/TitleToolTip=The title of the document, or the name given to the resource. Typically, it will be a name by which the resource is formally known.">
<fi:XMPTextInput id="docTitle"
xmpPath="mc:MainCategory
xmpType="Localized"
name="docTitle"
/>
</fi:XMPFormItem>
 The things of note here are the XMPTextInput which is mapped to a custom namespace mc:MainCategory.

So how does it work?

First we create a global array that will hold the list of categories that have been ticked.

private var cats:Array = new Array();

Then when each checkbox is ticked / unticked it calls the function updateCats(Event).
Because it passes an event, we can use the event.target to determine which box was ticked, and retrieve it's label and id.
Once we have these, we can add or remove it from the array as necessary.

Here's the code for the updateCats() function:

private function updateCats(event: Event):void

{

//we can use the event.target to identify the control that triggered the function :)

var catName:String = event.target.label;

var outStr:String;

var cName:String;
var delItem:Boolean;
if (cats.length == 0)
{
cats.push(catName);
} else {
for (var i:int = 0; i < cats.length; ++i) {
if (cats[i] == catName)
{
cats.splice(i,1);
delItem = true;
}
}
if (!delItem)
{
cats.push(catName);
}
}
outStr = cats.toString();
docTitle.text = outStr;
docTitle.dispatchEvent(new Event(Event.CHANGE));
}

Once the array has been update, we then convert it to a string and set the text field to display that string. Finally we tell the text field that it's been updated by sending it a change event.

It's not the most elegant code, but it works!

So, onto the second problem, how do we tick the boxes when the panel loads?
Because we have given each checkbox an id, we can reference them in code with this as:
this[elementID];

As we know that we have given the checkboxes id's that follow _chkFieldName, we can easily reference these.

When the panel has completed loading, we call another function called catsReadHandler() that reads the text field, and if it is not empty creates the cats array and then cycles through the array and ticks the boxes.

Here's the function:

private function catsReadHandler(event:Event):void

{

/*  This is called once the panel has completed initialising.

This will update the checkboxes to indicate which ones are ticked, 

and will populate the cats array

*/
var outStr:String;
var catsString: String = docTitle.text;
var chkName:String
var catStr:String;
if (catsString.length > 0)
{
cats = catsString.split(",");
for (var i:int = 0; i < cats.length; ++i) 
{
chkName = "_chk";
catStr = cats[i];
catStr.replace(/s+/g, "");
chkName += catStr;
try {
// this["control id"] allows us to reference the checkboxes
this[chkName].selected=true;
} catch (someError:Error)
{
docTitle.text=i + " : " + " " + chkName + " " + someError.message;
}
}
}
}

One important part is:


catStr.replace(/s+/g, "");

Which removes any spaces in the name of the check box, ie turning Road Haulage into RoadHaulage.
We then append this to _chk to get the name of the checkbox!

Here's the complete file, feel free to play around with it! categories.mxml

I'll be happy to answer any questions, but please bear in mind this is my first attempt at programming in flash / flex / actionscript. Of course, any suggestions for improvement will be welcomed !!





No comments:

Post a Comment