Editable LDMs
Mac/PC Text
RTF
Multiuser
Trigonometry
2D Vectors

Editable Text in Linked Director Movies

Source files: LDM.zip (Windows) | LDM.sit (Macintosh).

You can create a #movie member by importing one Director movie into another movie with the Link to External File option. You can then place the #movie member in a sprite on the Stage. If you set the member's scriptsEnabled property to TRUE, almost all of the interactivity of the original movie is maintained. Such a movie member is often known as an LDM: a Linked Director Movie.

There are, unfortunately, limitiations to interactivity. One of the major drawbacks is that you cannot edit text in the linked movie. This article shows you how you can work around this limitation.

The technique is quite simple, in theory. The linked movie and the host movie share a #text member, which is stored in an external cast that is linked to both movies. This shared member is placed in a second sprite on the Stage of the host movie. This sprite is positioned over the sprite that contains the linked movie member, immediately above its counterpart in the linked movie. When you edit the member in the sprite on the Stage of the host movie, the changes are immediately reflected in the linked movie.

You won't need to have any prior experience of Linked Director Movies. As I discuss the particular issues of editable text in an LDM, I'll also be introducing the general concepts, and touch on scope, encapsulation, and other Object Oriented topics, as well as giving tips on prototyping a new feature and on version control.

Proving the concept (top)

Let's start by proving that the shared member technique is workable.

  1. Create a new movie
  2. Create an external cast linked to the movie
  3. Create a #text member in this external cast, set it to editable and place it on the Stage
  4. Save the cast as LDM_01.cst and the movie as LDM_01.dir

  1. Create another new movie
  2. Use File | Import to import the LDM_01.dir and LDM_01.cst files. The linked movie will appear in the Internal cast, the linked cast will be added as an external file.

  1. Drag the LDM_01 member from the Internal cast onto the Stage
  2. Drag the #text member from the external cast onto the Stage. Don't try to align the sprites yet.
  3. Run the movie and type into the editable text sprite.

Enabling scripts and cropping (top)

One of the main reasons for using a linked movie is that visible area of the sprite can be smaller than the area of the linked movie's stage. The linked movie sprite gives you a viewport into a larger area. You can use scrollbars to move the linked movie's sprites around. This clips the sprites in the linked movie at the edges of the sprite on the host movie's stage.

However, by default #movie members are set to scale when you resize the sprite on the host movie's stage. You can see the effect of this in the illustration above: the sprites in the linked movie are squeezed or stretched.

Shortly, we're going to be using code inside the Linked Director Movie. In order to get this code to execute when the movie is running inside a host movie, you'll need to enable the scripts.

Both the .crop and .scriptsEnabled properties of the #movie member need to be set to TRUE. You can do this using the Property Inspector or with Lingo.

Save your new movie as Host_01.dir, then reopen the LDM_01.dir movie. We're going to add more text sprites and a behavior.

Working with multiple #text members (top)

To create a simple spreadsheet, we are going to need more than one #text member in the Linked Director Movie. However, we don't want to have multiple sprites on the host movie Stage or multiple #text members in the shared external cast. That would defeat the purpose of using an encapsulated #movie member. We need to be able to share the one editable text sprite in the Host movie with any of the text sprites in the linked movie. This means that the shared #text member needs to know which sprite in the linked movie it is working with at any given time.

In the Internal cast of the LDM_01.dir movie, create two new #text members. Call them "Cell 1" and "Cell 2" and type different text into each of them. Set the .boxType property of the members to #fixed and the .editable property to FALSE. Remove the existing sprite from the stage and replace it with sprites made from these two new #text members.

Select an empty cast slot in the External cast, then select both text sprites on the Stage, right-click on them (Windows) or Control-click (Macintosh) and choose Script... in the contextual menu that opens. This will add a behavior to both sprites: the behavior #script member will be in the External cast. Writing and debugging the script will thus be much easier, since the scriptText will be still be available when you are working in the Host movie. Indeed, we'll write the behavior entirely from within the Host movie.

Save your movie as LDM_02.dir. The shared external cast will have changed too, but it will retain the same name. Now reopen the Host_01.dir movie.

Because you are working with linked files, you will need to change the link in the host movie. You can do this by selecting the Member tab for the #movie member in the Property Inspector, and clicking on the fileName button.

Note that the name of the member will not update to reflect the change in the fileName.

Using static variables (top)

When you are working with Linked Director Movies, it helps to avoid using global variables. If you have two sprites showing the same #movie member, and both use the same global name, data will leak from one sprite to the other, leading to confusion. The simplest solution is to use the properties of a #script member to store any variables that the movie in given sprite needs to remember.

In the LDM_01 cast, create a new #script member and call it "Cell". Set the .scriptType of the new member to #parent. We'll write the code for this in a moment. This script will encapsulate the memory of the LDM sprite. To tell the truth, the way I'll be using it at first is not rigourous. For encapsulation to work, the script must be in an internal cast of linked movie. Since we're placing it, for now, in the shared cast, it is just as leaky as a global variable.

A better solution would be to use a script instance on the actorList. There is a unique actorList for each movie, whether it be playing on the Stage, in a sprite or in a window.

Setting the focus of the editable sprite in the LDM (top)

Open the behavior that you attached to the two text sprites in the LDM_02.dir movie, and enter the following scriptText:

on mouseDown(me)
  script("Cell").SetFocus(sprite(me.spriteNum))
end mouseDown

Regardless of which text sprite you click on, the mouseDown will be treated in the separate "Cell" script. The "Cell" script can thus keep a record of which sprite currently has focus, so that it can clean up before transferring the focus to a new sprite.

Here's are the properties and handler for the "Cell" script:

property pOpenSprite
property pOpenMember
on SetFocus(me, aSprite)
if pOpenSprite = aSprite then
-- This cell already has focus
exit
end if
tSharedMember = member(1, 2) -- HARD-CODED for now
if voidP(pOpenSprite) then
-- This is the first cell to be given focus
else
-- Remove focus from the current cell
pOpenMember.html = tSharedMember.html
pOpenSprite.member = pOpenMember
end if

-- Adopt the shared member
pOpenSprite = aSprite
pOpenMember = aSprite.member
pOpenSprite.member = tSharedMember

-- Copy data over to the shared member
tSharedMember.html = pOpenMember.html
tSharedMember.font = pOpenMember.font
end SetFocus

The SetFocus() handler copies the contents of the member the user wants to edit to the editable member in the shared cast, then swaps the editable member into the sprite the user clicked on. When the user wants to edit a different sprite, the sprite that is losing focus re-adopts its original member, and the contents of the original member are read back in from the shared member. Using the .html property ensures that any formatting is also copied over. Setting the .font property ensures that any additional text is in the expected font.

Run the movie and test what happens if you click on each sprite in turn. Since we are making no attempt to move the editable sprite on the host movie Stage, the result is like having a separate input field in a spreadsheet application.

Forcing an update (top)

Test the Shockwave version below. You may notice that the height of the sprite being edited changes, if the .boxType of the original member is different from tIf you edit the text of one cell, you will need to click on the other cell before the contents are updated. This is due to the fact that the movies are running in a browser. In the version you are building in Director, both instances of the shared #text member should update automatically, though there may be a slight lag for the instance in the linked movie.

Understanding scope (top)

The lag (or lack) of the automatic update can be hidden by placing the text sprite in the host movie at the same screen position as the appropriate sprite in the linked movie. This means that the linked movie has to be able to communicate with the host.

The approach I use here is to ensure that code in the Linked Director Movie deals with what happens inside the Linked Director Movie,while code in the Host movie deals with what happens on the Stage. Communication consists simply of one movie telling the other what it should do, then leaving it to do it. In other words, the code executed in each movie is restricted to the scope of that movie. The fact that all the code for the feature can be stored in the same shared external cast is a bonus.

Version control (top)

As you saw earlier, saving a new version of a file under a different name can create version control problems. In the Host movie, you explicitly had to change the file path to the new version of the linked movie. Let's use a different technique, which maintains the links but uses more disk space.

Create a new folder and copy all the files you have created into it. You can name the folder "Editable LDM". You can rename the files within it as LDM.dir, Shared.cst and Host.dir. You'll need to reset each of the links: to the shared cast and the linked #movie member. From now on, each time you want to save a backup version, you'll simply save a copy of the entire folder and rename that, using a version number. This technique means that absolute file path to your working copies never changes, so you can be sure that the links remain intact. It is a technique that I use for all my projects, whether they used linked files or not.

Communicating with the Host movie (top)

In the Host movie, select an empty cast slot in the Shared cast, right-click or control-click on the LDM sprite on the Stage, choose Script... in the contextual menu, and paste the following scriptText into the behavior that is created.

property spriteNum
property pSprite
property pEditable


on beginSprite(me) pSprite = sprite(spriteNum) pEditable = sprite(spriteNum + 1) tell pSprite SetLDMPort(me) end tell end beginSprite


on SetEditableLoc(me, aLoc) pEditable.loc = aLoc + [pSprite.left, pSprite.top] end SetEditableLoc

Call this new behavior "LDM sprite behavior". Create a new movie script member in the Shared cast. This movie script will also be available to the linked movie:

on SetLDMPort(anInstance)
  script("Cell").SetLDMPort(anInstance)
end SetLDMPort

When the LDM sprite behavior is instanciated, it calls the SetLDMPort() handler in the movie script inside the LDM.dir movie, which in turn calls a handler in the "Cell" script. To get this to work, you'll need to edit the "Cell" script, and add a property declaration...

property pStageInstance

... add a new handler ...
on SetLDMPort(me, anInstance)
  pStageInstance = anInstance
end SetLDMPort
... and add a few lines to the end of the SetFocus() handler:
  -- Ask the LDM sprite behavior on Stage to move the editable sprite
  tLoc = pOpenSprite.loc
  tell the stage
    pStageInstance.SetEditableLoc(tLoc)
  end tell
end SetFocus
      

So what effect do these changes have? When the LDM sprite behavior is instanciated, it gives the Cell script in the linked movie a mobile phone number. This is its "me" property, which tells the Cell script where to find it in the computer's RAM space. When the user clicks on a cell sprite, the Cell script uses that information to call the LDM sprite behavior and ask it to move the editable sprite on the stage. The LDM sprite behavior needs to convert the local co-ordinates that it is given to Stage co-ordinates, by adding the top-left point of the LDM sprite to the loc of the sprite inside the LDM.Dir movie.

Disposing of outdated property values (top)

When you first run your movie, you will have recompiled the Cell script, and everything should work fine. When you next run it, you may find that you have to click on a particular sprite in the linked movie in order to get the editable sprite on the Stage to move as it should. The reason for this is that the properties in the Cell script are maintained even after the movie is stopped. To test this, watch the following expressions in the Watcher window. (Note that these values are only accessible because the Cell script is not in the LDM.dir movie's internal cast: we are exploiting the leakiness that I mentioned earlier).


Stop the movie. The values remain. Restart the movie. The pOpenSprite and pOpenMember values are maintained. To deal with this, you can add two new handlers. Add the following handler to the movie script member you created in the Shared cast:

on stopMovie()
script("Cell").CleanUp()
end stopMovie
And add this handler to the Cell script:
on CleanUp(me)
pOpenSprite = VOID
pOpenMember = VOID
pStageInstance = VOID
end CleanUp

Try running several cycles of staring your movie, clicking on one or other of the sprites in the LDM sprite, and stopping the movie. Now that you are disposing of outdated property values correctly, the problem should have cleared up.

Conclusion (top)

This article has shown how to work around the fact that text inside Linked Director Movies is not directly editable. The demonstration movies in the download files are somewhat more complex than those described here. Indeed, you will find two versions of the movies: one uses the static script technique explained here, the other uses the actorList in a variety of ways to achieve the same ends.

The ideas described here could be taken further. You might, for instance, want to have several LDM sprites sharing the same editable text sprite in the host movie. This implies that each LDM sprite is aware of its own state of focus, so that it can swap out the shared #text member as soon as it looses focus. You can place most of the scripts in the Shared cast in the Internal cast of the LDM movie, leaving only the behavior used in the host movie and the shared #text member. Indeed the behavior used in the host movie can be made generic so that it provides a communications channel to any Linked Director Movie that has been built to a particular standard. You can find an examples of this here.


Source files: LDM.zip (Windows) | LDM.sit (Macintosh).