Articles Archive
Articles Search
Director Wiki
 

Behavior Initializers 2: runPropertyDialog

January 24, 2001
by Brennan Young

The first article in this series covered the getPropertyDescriptionList handler in about as much depth as most people would want. This article is going to concentrate on other ways to access the initializers of sprites with attached behaviors.

I'll start where I left off. Now that we understand how to construct a getPropertyDescriptionList handler, it's useful to understand exactly when it is called. In fact it's called a lot more often than most people expect, which can be a bit of a nuisance.

As mentioned in the first article, getPropertyDescriptionList is called at author time, not at runtime.

This is unusual, but necessary, and it applies directly to a slightly higher level of authoring than working with lingo alone - we might call it 'configuration' of the system. Often, on basic multimedia projects, I will spend the first 15% of the time creating behaviors, then most of the rest of the time configuring them (or farming the task out to a monkey).

This means that the scriptInstanceList of the sprite is empty when the handler runs. It also means that whatever is going on, is not going on in the instances which end up in that list. Of course, as the title of this series suggests, it's going on in the behavior initializers of the sprite.

Given that there is no instance as such, just some barely tangible property lists, it's important to be clear what the me parameter represents when the handler runs, and how to access the sprite where the behavior is being attached or configured.

If you include the me parameter in your getPropertyDescriptionList handler, (which is optional), me contains a reference to the script object (i.e. the bytecode class) of the behavior. It's in the form (script "xyz") where "xyz" is the name of the behavior.

This is useful, because it gives you direct reference to all the properties and handlers of the behavior, so your behavior initializer handlers can call other handlers in the same behavior if necessary. Adventurous lingo hackers might also want to mess around with the values of the properties of the script object.

As for the sprite, well given that we are not working with an instance proper yet, we can't use the spritenum of me. What we do instead is use the currentSpritenum a system property which is almost always the same as the spritenum of me. It contains the sprite channel number of the sprite which received the most recent message.

That means that when getPropertyDescriptionList runs, we have access to the behavior that contains it, and the sprite where it is being attached (if any).

The getPropertyDescriptionList handler is called as a result of three different actions when authoring.

1) Every time you recompile all scripts;

At this time, the currentspritenum has the value 0. It's often a good idea to trap this if you are 'intelligently' setting up default values in your getPropertyDescriptionList handler. The standard way to do this is:

on getPropertyDescriptionList me

  if the currentSpriteNum > 0 then -- we actually have a sprite
    set defaultMemName = the name of the member of sprite the currentSpriteNum
 
else -- it's just a recompilation
    set defaultMemName = ""
  end if
  -- etc...

If you don't do this, you may get error messages complaining about objects not being found. In this case, the member of sprite 0 is (member 0 of castLib 0), which does not exist, so it can't have a name either.

In some cases, the getPropertyDescriptionList handler is called twice, (with the values of me and the currentSpritenum being the same). The exact circumstances that lead to the handler being called twice differ from version to version of Director and can be (for example) whether the behavior script window is open and/or frontmost, whether you use the 'recompile' button, menu item, keyboard shortcut or contextual menu item.

I was going to prepare a table documenting all these fine details, but I figured this knowledge was too esoteric (and tedious) to present. The main thing is that it does get called twice sometimes, and we'll see why that might be important later.

2) When you attach a behavior to a sprite;

At this time, the currentspritenum has the value of the sprite channel of the sprite you are attaching it to.

The handler is called twice. The reason for this concerns the runPropertyDialog handler, which we are about to look at in more detail.

3) When you open the parameter dialog again using the behavior inspector (or the property inspector in Director8 or later) for a sprite where the behavior is already attached.

Again, the currentspritenum has the value of the sprite channel where you are attaching it. It is called once only in this case.

The runPropertyDialog Handler

This is a mysterious piece of kit. It's mostly useless, poorly documented, and confusing, so if you've read the documentation and wondered why you didn't quite get it, it's not your fault.

It does, however, have some uses. There are situations where entering data into dialog boxes is extremely tedious. The runPropertyDialog handler enables you to semi-automate the configuration of sprite behaviors so that you don't see the dialog box at all.

One of the most confusing things about this is that the runPropertyDialog handler does not get called at all if the getPropertyDescriptionList handler is not present.

Just to be clear: The runPropertyDialog handler allows you to bypass the dialog box created by the getPropertyDescriptionList handler, but if that handler is not present, the dialog box will not be created anyway.

This doesn't appear to make any sense at all until we remember that the getPropertyDescriptionList handler gets called twice when you attach a behavior.

The first time, Director is getting the structure of the behavior initializer which will be stored in the sprite. Once this has been done, a more simple property list is fed to the runPropertyDialog handler if present.

This means that the runPropertyDialog handler needs the getPropertyDescriptionList handler in order to work properly. It takes a second parameter, the simple property list representing the properties settable by the dialog box, and their current (default) values. For runPropertyDialog to be useful at all, it needs access to this parameter.

The job of runPropertyDialog, then, is to automate the configuration of properties for the sprite, then return the adjusted property list to the system. If this happens, the dialog box will not open at all.

Here's a useless bare bones example;

property a

on runPropertyDialog me, pdlist

  setAprop pdlist, #a, 99
  return pdlist

end

on getPropertyDescriptionList me

  set pdlist to [:]
  addprop pdlist, #a, [#comment:"a", #format:#integer, #default:0]

  return pdlist

end

But why would you want to do this? Surely just setting the property to 99 in the beginsprite handler would be exactly the same.

Well, the trick here is that the runPropertyDialog handler does have the power to run the parameter dialog if desired, by using the pass control statement:

on runPropertyDialog me, pdlist

  if the shiftDown then
    
    setAprop pdlist, #a, the ticks
    return pdlist
    
  else
    pass -- run property dialog as normal
  end if

end

We see pass used in a slightly similar way in key event handlers. It feels a bit strange when Director gets control back like this.

Note that any lingo following a pass statement will be ignored. It is similar to return in that respect. Note also that you can not pass a modified property list back to Director; pass takes no parameters.

In this example, you could use the behavior completely as normal, setting the property a in the dialog box, but if you wanted to set a to the ticks automatically, all you'd have to do was hold down shift and click on the parameter button in the behavior inspector.

Let's take a more practical and useful example. I often need to have sprites which can be made visible or invisible. They don't need to move about, just be there or not there at my beck and call. We might imagine an alert box which drops in over the main content.

I've been burned too many times using the visible of sprite (it affects the entire sprite channel, not just individual sprites, which can cause unpredicatble results). For that reason, I often make a behavior which 'hides' and 'shows' sprites by moving them on and off stage:

property spritenum, shownPos

on hide me
  set the loc of sprite spritenum = point(-999,-999)
end

on show me
  set the loc of sprite spritenum = shownPos
end

  
on getPropertyDescriptionList me

  if the currentSpriteNum > 0 then -- we actually have a sprite
    set p = set the loc of sprite the currentSpriteNum else
    set p = point (100,100)
  end if

  set pdlist to [:]
  addprop pdlist, #shownPos, [#comment:"Appear where", #format:#point, #default:p]

  return pdlist

end getPropertyDescriptionList

As you can see, I've added some code in the getPropertyDescriptionList handler which uses the sprite's location as it is in the score as the default shownPos. This is very convenient, and ensures that the sprite will always return to the correct place even if we move it offstage during authoring (for example if we had layers underneath which needed to be laid out).

If necessary I can type in a particular point value to have it appear somewhere else, but for layout purposes, it's useful to use the stage/score and then attach this behavior without typing any numbers at all. Now, let's imagine that we want to adjust the position where we want the sprite to appear. We could position it manually on the stage, look in the sprite inspector, read the locH and locV, get the dialog box open and then type the values in, taking extra care not to mess up the syntax of the point value (see the previous article about this). If we wanted to do this with a lot of sprites, it would be extremely tedious.

Or we could use runPropertyDialog to automate the process:

on runPropertyDialog me, pdlist

  if the shiftDown then
    
    set p = set the loc of sprite the currentSpriteNum setAprop pdlist, #shownPos, p
    return pdlist
    
  else
    pass -- run property dialog as normal
  end if

end

Simply moving the sprite, then shift-clicking on the parameters button will update the initializer value. We can now move the sprite offstage using the Tweak tool. If we ever need to adjust the position again, just tweak it back on stage, adjust the position, then shift-click the parameters button again. No typing necessary.

Another example might be a behavior which links to some external file using a pathname. We could use the FileIO Xtra's displayOpen function to get the file name, but if you try this in the getPropertyDescriptionList handler, you'll drive yourself crazy because that handler gets called more often than is useful. In particular it gets called twice when you first attach the behavior, so only the second file dialog box is of any use. Moving it into runPropertyDialog solves the problem:

property externalFile

on runPropertyDialog me, pdlist

  if pdlist.externalFile <> "" and not the shiftDown then
    pass -- run property dialog as normal
  end if

  -- else...

  set x = new(xtra "fileio")
  set fname = displayOpen(x)

  if fname = "" then
    -- user cancelled
    pass -- run property dialog as normal
  end if

  pdlist.externalFile = fname
  return pdlist
end

on getPropertyDescriptionList me
  set pdlist = [:]
  addProp pdlist, #externalFile, [#comment:"externalFile", #format:#string, #default:""]
  return pdlist
end

In this example, the first time you drop the behavior on the sprite, the displayOpen file selection dialog box comes up. Thereafter, the ordinary property dialog box will open displaying the path name as a string.

If they hold down the shift key when accessing the parameters an external file can be chosen using displayOpen again.

As you can see, the uses of runPropertyDialog are rather specialised, and you can certainly live without it, but these examples are also very simple. If you ever need to set the parameters for a sprite behavior to the result of some very particular algorithm, using runPropertyDialog can be very handy.

The key point in using it effectively is understanding the role of pass and having some mechanism (such as shift-clicking) to override or force the dialog box to open when necessary.

Brennan Young is an English freelance multimedia designer / programmer living and working in Copenhagen, Denmark. He started making interactive presentations on Commodore and Acorn computers as a teenager in the early 1980s, but went on to study Fine Art and Art history at Goldsmith's College, London where he discovered Director. He teaches and lectures on multimedia at various institutions around Denmark. When he is not fiddling with interesting authoring software, he composes and performs music, writes theoretical and practical articles, and 'makes art' for exhibitions or private consumption.

Copyright 1997-2017, Director Online. Article content copyright by respective authors.