YOUR FEEDBACK
Kirstan Vandersluis wrote: Great intro to SOA, Paul. You mention in the summary that budget cuts may stifl...


2008 East
DIAMOND SPONSOR:
Data Direct
Frontiers in Data Access: The Coming Wave in Data Services
PLATINUM SPONSORS:
Red Hat
The Opening of Virtualization
Intel
Virtualization – Path to Predictive Enterprise
Green Hills
IT Security in a Hostile World
JBoss / freedom oss
Practical SOA Approach
GOLD SPONSORS:
Software AG
The Art & Science of SOA: How Governance Enables Adoption
PlateSpin
Effective Planning for Virtual Infrastructure Growth
Fujitsu
Automated Business Process Discovery & Virtualization Service
Ceedo
Workspace Virtualization
Click For 2007 West
Event Webcasts

2008 East
PLATINUM SPONSORS:
Appcelerator
Think Fast: Accelerate AJAX Development with Appcelerator
GOLD SPONSORS:
DreamFace Interactive
The Ultimate Framework for Creating Personalized Web 2.0 Mashups
ICEsoft
AJAX and Social Computing for the Enterprise
Kaazing
Enterprise Comet: Real–Time, Real–Time, or Real–Time Web 2.0?
Nexaweb
Now Playing: Desktop Apps in the Browser!
Sun
jMaki as an AJAX Mashup Framework
POWER PANELS:
The Business Value
of RIAs
What Lies Beyond AJAX?
KEYNOTES:
Douglas Crockford
Can We Fix the Web?
Anthony Franco
2008: The Year of the RIA
Click For 2007 Event Webcasts
SYS-CON.TV
MXDJ TOP LINKS YOU MUST CLICK ON !


Talking to Sprites
Talking to Sprites

Tabs are a basic interface element that Director has tended to ignore. Director MX 2004 introduces a series of new FlashComponent member types, to renew and extend the built-in controls, but there is no tab member to be found among them.

Tabs combine organization and navigation. They indicate to the user that the current interface provides access to a related set of concepts. For example, your project may require a Preferences window. You could provide one tab for each theme that your preferences need to cover. When the user clicks on a tab, the appropriate screen will appear. Each screen will contain its own input fields, checkboxes, and other buttons.

This article shows you how to create a set of tabs using customized bitmap members and a simple generic behavior. You'll be learning about:

  • Matte ink
  • Testing commands in the Message window
  • Sprite channels and locZ
  • Properties and variables
  • Variable naming conventions
  • Events
  • Moving between markers
  • Sending messages between sprites
  • Behavior instances
  • Lists
You can get a preview of the movie that you are going to create at http://nonlinear.openspark.com/tutorials/tabs.dcr. You can find the completed movie at: You can download a demo copy of Director MX 2004 from www.macromedia.com/go/try_dmx2004

Proof of Concept

In the first half of this article, you'll create a very simple Tab behavior, just to show that it's possible. In the second half, you'll build a more elaborate behavior which will merit a place in your code library.

Getting Started
Let's start by creating a simple movie with three placeholder screens.

1.  In the Score window, add markers named "Marker 1", "Marker 2", and "Marker 3" to frames 2, 32, and 62.

2.  Double-click on the Script channel in frame 28, and enter the following script in the window that opens: Create a behavior with the handler...

on exitFrame
go loop
end

3.  Drop the new behavior member on the Frame Script channel of the Score in frames 58 and 88.

4.  Using the rectangle tool from the Tool Palette, draw a rectangular Shape sprite on the Stage, in channel 1 and starting in frame 1, and extending to frame 28.

5.  Select the new sprite in the Score window, and Alt-drag it (Windows) or Option-drag it (Macintosh) to create new sprites in frames 31 to 58 and again in frames 61 to 88.

6.  Using the Property Inspector at the Sprite tab, set the color of these three Shape sprites to red, green and blue. This allows you to tell which section of your movie is playing at any given time.

7.  Set the color of the background to something other than white, so that you can create a highlight lighter than the stageColor. I chose color 200 from the Mac System palette - rgb("#006699").

This simple movie is enough for you to test the tabs feature. If you want to make it more like a Preferences window, please feel free to add your own input fields, buttons, and other controls at each of the markers.

Creating the Bitmaps
Let's start by creating a set of bitmap members, one for each of the three tabs that you are going to need. I created my tab bitmap in the Paint Window, in eight steps (see Image I):

  1. Create the text.
  2. Draw a box around it in a color lighter than the stageColor - rgb("#0099CC") in my case.
  3. Draw a darker line down the right-hand side - rgb("#003366").
  4. Remove the top corners.
  5. Drag the a 3x3 square in the top corners in and down to create a rounded appearance.
  6. Fill the box with the same color as the stage.
  7. Color the bottom of the box in the same color, including the bottom-right pixel.
  8. Add a line to either side of the tab in the same color I used in step 2, to make the bitmap the same width as the stage.
If you prefer, you can use icons instead of text. When you are satisfied, drag the bitmap member onto the Stage, and set its ink to Matte using the Property Inspector (see Image II).

Make two other bitmap members for the other tabs. I simply created a copy of the original and edited that (see Image III).

Name each bitmap member with the name of one of the markers in the Score window. You'll see why we use marker names for the bitmap members in a moment.

Interacting with the Tabs
Place all three bitmaps on the Stage using Matte ink. Notice that the tab in the highest-numbered channel appears to be in front of all the others. I'll assume that you have your "Marker 1" tab in sprite 2, "Marker 2" in sprite 3, and "Marker 3" in sprite 4.

Run your movie, and type the following command in the Message window:

sprite(2).locZ = 5

The tab in sprite 2 should now appear in front of the others. Try again, this time setting the locZ of sprite 3, and then sprite 4 (see Image IV). What happens when you try using sprite 2 again?

Normally, the order in which sprites are drawn on the Stage depends on the sprite channel they are in. A sprite in channel 2 will appear in front of a sprite in channel 1. When you move a sprite around the stage, you change the value of its locH and locV properties - its horizontal and vertical location. The locZ property affects the layer in which the sprite appears.

As you have seen, you can force Director to draw the sprites in a different order. When you set the locZ of a sprite to integer number, you tell Director to treat it as if it were in the channel with that number. You can thus give several sprites the same locZ value. However, Director will still distinguish between sprites with the same locZ: it uses their original sprite channel numbers to determine the order. Now that sprites 2, 3, and 4 all have a locZ of 5, sprite 4 will appear in front of the others.

In order to make sprite 2 appear in front, you have to set the locZ of the others back to their original value. By default, the locZ of a sprite is the same as the number of its sprite channel.

How a Behavior Knows Which Sprite It's In
Lingo has a specific keyword for this number: spriteNum. In a behavior, you can declare spriteNum as a property, and then use sprite(spriteNum) to refer to the sprite to which the behavior is attached. The property declaration must occur before any reference to the property. Traditionally, all the properties used by a script are declared at the beginning.

Creating a Simple Behavior
Let's make the tab sprites react to a click. Select all three tab sprites, then right-click (Windows) or Ctrl-click (Mac) and select the Script item in the contextual menu that opens. This will open the Script window. Enter the code that appears in Image V.

Note that this is a quick and dirty piece of code. It uses hard-coded sprite numbers. This is bad practice. If you move your tab sprites to a different set of channels, your behavior will no longer work. We'll see in a moment how to make the code more generic.

When you click on the sprite, Director sends it two messages: #mouseDown and #mouseUp. In this case, you don't want to do anything when the user first presses the mouse, so you ignore the #mouseDown message. The on mouseUp handler is the recipe that tells the sprite what to do when the user releases the mouse button over the tab sprite.

The me parameter is Director's way of referring to the behavior itself. When you attach a behavior to a sprite, you create a connection between that sprite and a script member. When the Director playback engine first meets the sprite, it creates an instance of the script. Each instance shares the same code, but is stored in a separate place in the computer's Random Access Memory. The me parameter stores the address in memory used for that particular instance on that particular sprite.

The word "behavior" is often used ambiguously. Sometimes it means "the script itself"; sometimes it means "an instance of the script in RAM". In this article, I use the words "script" and "instance" explicitly where it is necessary to draw the distinction between the two meanings. We will be taking a closer look at me in a moment.

Now run your movie and click on each of the tabs in turn. They pop to the front, just like the real thing. However, the playback head stays at the same marker. You need to add another line of code:

tSprite=sprite(spriteNum)
go marker(tSprite.member.name)

The name of each tab bitmap member is the same as the marker that you want to display when the user clicks on that tab sprite. You can therefore use the name of the member to jump to the appropriate marker.

Using Variables
Did you notice that you are now using sprite(spriteNum) twice? This forces Director to go back and consult its internal look-up tables a second time. It's much more efficient to save the value for the sprite in a variable, and use the variable instead.

In Lingo, a variable is simply a container; you can put any Lingo value into it. You don't have different variable types for numbers, text strings, or sprite references. In Image VI, I have used two variables: tSprite and tMarker. The "t" at the beginning of the variable name stands for "temporary". We will create more permanent properties whose names will begin with a "p", shortly. You don't need to use prefixes like this. Director doesn't understand prefixes. Humans do, however, and it can be very helpful when debugging your code to use a variable naming convention.

A temporary variable doesn't need to be declared. It's like a scrap of paper that Director takes notes on while working inside a particular handler. Once the handler is finished, Director throws the scrap away.

Why Use Matte Ink?
I told you earlier to use Matte ink for your tab sprites. Let's see why. Select all your tab sprites and set them to copy ink. Marker 3 covers the others with a band of white. Try Background Transparent ink.

The sprites now look good, but what happens when you run the movie? Try clicking on the "Marker 2" tab. The playback head jumps to Marker 3. Why? Because although the white background of the sprite is not visible, Director uses the entire bounding box of the bitmap to detect clicks.

With Matte ink when you click on a sprite, Director ignores any white pixels that are not enclosed by non-white pixels. It lets the click pass through to any sprites in lower-numbered channels. Even though the "Marker 3" sprite covers the other sprites entirely, Director lets the mouse events reach the lower-numbered sprites in the area where the "Marker 3" sprite is transparent.

Matte ink is the only ink that affects the way mouse clicks are detected. Other inks affect the way Director draws the sprite.

Time Out
You now have all the code you need to implement a Tab feature in a Director project. If you are Lingo intolerant, you can stop reading this article now. However, the next time you need to use tabs, you'll have to rewrite your behavior script using the appropriate hard-coded sprite channel numbers. Wouldn't it be better to do a little more work now, so that the next time you need to use tabs, you don't have to open the Script editor? If you think so, read on.

Making the Behavior Generic

To make the behavior generic, you need to ensure that it works regardless of where the tab sprites appear. That means that the behavior has to be able to learn where it is, and where the other sprites with the same behavior are.

Custom Events
A sprite can tell which channel it is in using the spriteNum property. But how can one tab sprite know where the other tab sprites are, so that it can move in front? One technique would be to send a message to all the other sprites, telling them to reset their locZ values. The sendAllSprites() command allows you to do this.

When you click on a sprite, Director automatically sends it a #mouseUp event, which is intercepted by the on mouseUp handler. You can create your own custom events and match them up with custom handlers with the same name.

Try this in your behavior script using the code shown in Image VII. The on mouseUp handler will now send a #ResetLocZ event to all sprites, and the on ResetLocZ handler knows what to do with it.

First it prints the spriteNum of the sprite that received the event in the Message window, along with information on where that sprite's behavior is stored in the computer's memory (the me parameter). Note that the sprites receive the message in order, the lowest-numbered sprite first. The value of locZ has no effect on this order. Note also that, although all the sprites share the same behavior script, the value of each instance's spriteNum property is different.

Second, the handler resets the locZ of the sprite to the same value as its spriteNum, which is the bit we are interested in.

You now have only one hard-coded value left in the behavior script: the value 5. What happens if you leave the script as it is and move the sprites up one channel?

Tip: Select the three tab sprites on the Stage or in the Score and press the Ctrl-Up arrow, or the Apple-Up arrow if you are on Macintosh. Now run the movie and click on the "Marker 2" tab. The playback head jumps to Marker 2, but the tab sprite does not appear in front of the sprite in channel 5 (see Image VIII).

Sharing Data with Other Behaviors
One way to avoid this issue would be to set the locZ of the selected tab sprite to the lastChannel + 1. This would move it in front of all the other sprites. However, if your application allowed the user to drag and drop items, they might pass behind the tab sprite, and this would look odd. In my experience, it is better to keep the tab sprites in low-numbered channels.

There is another drawback with the current technique: sendAllSprites() can badly slow down your movie. In the current case, this isn't obvious. I have encountered projects where one sprite used sendAllSprites() to broadcast one event, and the sprites that received the event sent out other events, again using sendAllSprites(), and so on. What looked like a single line of code ended up in a flood of messages that almost drowned the machine. If used incorrectly, SendAllSprites could become the Lingo equivalent of the chain letter.

How can the behavior instances determine which tab sprite is in the highest-numbered channel so that they can set the locZ of the front-most sprite to a higher value? The solution that I propose also reduces the use of sendAllSprites() to a single salvo: we'll use a list.

Lingo Lists
In Lingo, a list is like an address book: it contains information about where things are. Just as an address book does not contain the people it refers to, so a list variable does not contain the data itself. It contains the address where that data can be found in the computer's memory.

If you use two variables to refer to the same list, and then change the contents of one list, the contents of the other variable will change too. Try it in the Message window:

gList1 = []
put gList
-- []
gList2 = gList1
-- Change gList2...
gList2.add(#aValue)
-- ... and gList1 changes too:
put gList1
-- [#aValue]

Note: I use "g" as a prefix here because variables created in the Message window are global. Any script in any movie can access and alter the value of a global variable... so long as it is properly declared.

You're now ready to use this feature in your behavior. You'll find the final version of the script in Image IX.

There are six new Lingo expressions to learn about:

  1. if ... then ... end if
  2. listP
  3. not
  4. []
  5. getLast()
  6. call()
These are all explained in the Lingo dictionary and the online Director help. "[]" appears at the beginning of the Dictionary, among all the other non-alphabetical characters.

Basically, what happens now is this: when the behavior instances are first created, the property pTabList has no value in any of the instances. The first time you click on a tab sprite, that behavior instance realizes that pTabList is not yet a list. Obligingly, it creates a new, temporary list, and sends it out to all sprites on the back of a custom #Tab_SetList event. The custom event's name starts with #Tab_, so the chances are that only the Tab behavior will have a handler of that name. Instances of other behaviors in your movie will ignore it.

The on Tab_SetList handler in each Tab behavior instance receives the list in the aList parameter. The "a" prefix stands for "attribute", meaning that the value was sent from somewhere else, not created in the handler itself, like a temporary variable. When the on Tab_SetList handler is completed, the original tList variable will still be held in the computer's memory... just as long as it takes the initial on mouseUp handler to complete.

The on Tab_SetList handler in each behavior instance does two things: it sets its own pTabList property so that it refers to the list, and it adds a reference to itself to the list.

Each Tab behavior now knows about all the other Tab behaviors: each behavior has a pTabList property which stores all the behavior's addresses, in the order of their sprite channels. This means that the last entry in the list refers to the behavior on the sprite in the highest channel. Add 1 to the spriteNum of this last behavior, and you get a locZ in front of all the Tab sprites.

Instead of sending a message to all sprites, you can now simply send a message to the list of Tab behaviors.

Let's run through that again.

  • The behavior instances are all in different places in the computer's memory. The fact that each has a different value of spriteNum proves this.
  • All the behavior instances have their own pTabList property, but by sleight of code, all these different properties refer to the same list.
  • The contents of this unique list is a set of variables, each of which refers to one of the Tab behavior instances.
  • Because of the way Director talks to sprites, these instances are ordered by spriteNum, so the last instance is attached to the sprite in the highest-numbered channel.
  • You can use call() to send a message to all the instances in a list.
In other words, the pTabList is like a private club, where everyone knows everyone. The sendAllSprites() command is used just once to create the club.

Conclusion

You need to make one final tweak: reverse the order of the tab sprites so that the "Marker 1" tab appears in the highest-numbered channel. That way, the correct tab will be in front when you start your movie.

You now have a generic behavior that can be used in any sprite channel. The behavior in the download movie contains a couple of changes for reasons of optimization, and is much better commented. You may want to add it to your code library.

It's taken just 21 lines of code to make a robust behavior. How could this behavior be improved? Here's a brief shopping list of possible enhancements:

  • Create the tab members automatically with a given text label and icon
  • Use a single sprite
  • Do more than just jumping to a given marker
  • Move the appropriate tab sprite to the front if the user navigates to a marker other than by clicking on a tab
  • Add support for more than one group of Tab sprites on a given page

Where to Go from Here

MeccaMedialight has made available a widgets library containing open source code for creating Tab and other controls, via their LingoWorkshop site: www.lingoworkshop.com/code/widgets3_lib_tabs.php
About James Newton
James Newton works for OpenSpark Interactive Ltd. The company specializes in designing multimedia applications for improving production processes. His contributions to the Behavior Library, and his articles on Imaging Lingo, 3D mathematics, LDMs, the MultiUser Server and other Director topics have helped Lingo users at all levels.

LATEST FLEX STORIES & POSTS
As fate would have it, Adobe picked the worst possible quarter to roll out the great update to its flagship Creative Suite widgetry. Because the economy is tanking, it hasn’t been selling the way it was supposed to. As a result, revenues came up short in the November quarter and Adob...
It's a wonder it has the entry fee, but Sun Thursday showed up for the RIA race against Adobe’s Flex, Flash, and AIR and Microsoft's Silverlight – and for that matter the open source AJAX – riding JavaFX 1.0. According to Sun it's one of the most significant advances ever to come...
I spoke on a panel at Mashup Camp this week on why Ajax Standards matter. I was quoted by Doug Henschen of Intelligent Enterprise as saying that we are locked in a struggle for the soul of the web, so I thought I would expand on that theme. Just because the web has been open so far doe...
This is my final blog from the Adobe MAX 2008 conference.
Adobe and ARM are gonna put Flash Player 10 and AIR, the stuff of web video and rich Internet apps, on ARM widgets by the second half of next year. They mean phones, set-tops, MIDs, TVs, car mojo and personal media devices, which have so far only had access to Flash Lite, not the best ...
New releases of Flex software were announced at MAX. How Flash Catalyst works.
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS
SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
Click to Add our RSS Feeds to the Service of Your Choice:
Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
Publish Your Article! Please send it to editorial(at)sys-con.com!

Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021


SYS-CON FEATURED WHITEPAPERS

ADS BY GOOGLE