Smart Property Description Lists
July 27, 1999
by Chuck Neal
A while back I was reorganizing my scripts and behaviors that I had stored in libraries and realized that for some simple tasks I had as many as 5 or 6 different scripts to control each group of sprites. I started combining the functionality of a number of these, but then my getPropertyDescriptionList dialog boxes became cluttered and hard to deal with due to all the extra properties. Some of these were only used by a few pieces of the system. I started experimenting and applied some good old-fashioned Lingo to my on getPropertyDescriptionList handlers and it's now much easier to handle multiple functions in a single behavior.
Even without numerous additions, the getPropertyDescriptionList handler is a little overwhelming at first. Many beginners have a rough time figuring out the initial format, how to set ranges of values, etc., but the added functionality it gives your behaviors is well worth the effort. With a few lines of Lingo you can have a fully customizable dialog that allows user input of every variation of a behavior. This saves hours of recoding, redundant scripts, etc.
But the getPropertyDescriptionList handler is even more flexible than that. What if you wanted to have a behavior display different prompts to the user for different types of cast members? This is actually much easier than you might think. Like any Director handler, the on getPropertyDescriptionList handler accepts most any conditional statements allowing full control over what you feed it. Since the dialog box that it generates is compiled from a property list, we can custom generate this list based upon a few simple conditions.
For the purposes of this example, let's create a small system that will be used to control a field sprite and three buttons. Typically you would write two behaviours; one for the field and another for the buttons. While there is nothing inherently wrong with this approach it is much simpler to combine this system into a single behavior.
Our example behavior will control an editable text field that has 3 associated buttons.
- one button locks the field (toggles the editable property to FALSE)
- one button unlocks it (toggle the editable property to TRUE)
- and the third clears the field
Obviously we don't want to have the options for the button types display when we drop the behavior on the text field and, conversely, we don't want field options to display when we apply the behavior to the buttons. Using the member type property we can easily display only the appropriate options.
property fieldSprite, spriteNum, function property startState, clearStart, theType on getPropertyDescriptionList me p_list = [:] thisType = sprite(the currentSpriteNum).member.type if thisType = #field then addProp p_list, #startState, [#format : #boolean, ¬ #default : true, #comment : "Start with the field unlocked?"] addProp p_list, #clearStart, [#format : #boolean, ¬ #default : true, #comment : "Clear the field at startup?"] else if thisType = #button then addProp p_list, #function, [#format : #symbol, #default : ¬ #clear, #comment : "This button is to :", #range : ¬ [#clear, #lock, #unlock]] else -- Director will fire a getPropertyDescriptionList event so -- we need to add this so we don't get the error every time -- we recompile. Thanks John! if NOT symbolP(thisType) then alert "this behavior only works with fields and buttons" exit end if end if return p_list end
This script looks at what type of sprite the behavior is dropped on and displays the appropriate options for the user.
Notice that the property list we will return is declared before the conditional statement. This avoids having a void variable being returned as the property description list. As well, this allows for any variables we may want assigned to all of the objects to be initially declared. Once this is done we can then use the conditional statements to check the cast member's type and then display only the properties it needs.
Once the property list has been declared we just check the type of member in the beginSprite handler and determine what setup (if any) the behavior needs to do to the sprite it is attached to. Again we create some conditional code based on the sprite's member's type.
In this case
- if the sprite is a field it will
- set the editable to the state determined by the user
- clear the field's contents if the user wanted it cleared
- broadcast the sprite's number to the buttons so they can store that value for later use
- if the sprite is a button
- simply change the text of the button to match its function
on beginSprite me -- check the type of the sprite and act accordingly theType = sprite(spriteNum).member.type if theType = #field then -- for a field sprite we need to set the editable sprite(spriteNum).editable = startState -- and possibly clear the field's contents if clearStart = true then sprite(spriteNum).member.text = "" end if -- now tell all the button sprites -- which channel we are in sendAllSprites(#storeFieldSprite, spriteNum) else -- its a button as we tested for this earlier -- get the current member thisMember = sprite (me.spriteNum).member -- change the name of the button to -- reflect its functions case function of #clear: thisMember.text = "Clear" #lock: thisMember.text = "Lock" #unlock: thisMember.text = "Unlock" end case end if end on storeFieldSprite me, whatSprite -- store the sprite ref that the field sent to us fieldSprite = sprite(whatSprite) end
The storeFieldSprite handler simply records the field's spriteNum when it "broadcasts" it. This is a simple way to transmit data when you don't know the sprite numbers of the sprites that will need the data. In general though you should be aware of the performance penalties that using sendAllSprites will create.
The only thing that remains is to get the behavior to react to the user. As before, we use the same type of conditional statements to differentiate between the different elements in the system. When the user clicks on the field sprite we don't want to do anything. But when the user clicks on one of the three buttons we want it to react accordingly.
on mouseUp me if theType = #field then -- don't do anything for a field pass else if theType = #button then -- its a button so test for its type case function of #clear: fieldSprite.member.text = "" #lock: fieldSprite.member.editable = false #unlock: fieldSprite.member.editable = true end case end if end
This technique gets more useful every time I use it. It has taken my "out-of-control" behavior libraries and made them into nice neat pieces that are far easier to organize. You also remove the risk of misplacing one piece of the system you forgot to copy. I found this to be especially valuable when creating the behavior library on my site as I didn't have to cross-link 3 or 4 behaviors together. I used it in all of my ActiveX control behaviors as well, so the end user just needs to drop the script on everything, and doesn't need to know where everything goes, how the ActiveX works, etc.
There are a number of ways to further enhance this technique. Conditional statements can not only be based on the type of a member but also on names, the cast a behavior is in, the current behavior list, color, etc. Throw a few conditions into your getPropertyDescriptionList handlers and see what it can do for you.
A sample movie, in Mac or PC format, is available for download
Copyright 1997-2024, Director Online. Article content copyright by respective authors.