Chapter 10. Keyboard Access and Modifying Onscreen Text

CONTENTS

Chapter 10. Keyboard Access and Modifying Onscreen Text

  •  The Two "Text" Objects: TextField and TextFormat
  •  Selection Object
  •  Key Object
  •  Summary

In the preceding chapter, we explored how to manipulate strings. However, each example used either a literal (hard-wired) string or a variable that we assumed had been previously populated. This certainly is a common technique perhaps you fill an array with several words and then extract just one at a time. Quite often, however, you'll want to let users supply the string data (perhaps letting them type in their name and password). There are actually a lot of subtleties to how users can use their mouse and keyboard to input text. In addition, you have nearly infinite possibilities as to how you present that text onscreen (regardless of whether it's taken from the user). In this chapter, you'll learn both how to gather text the user inputs and how to display any text onscreen.

In this chapter, you will:

  • Use the TextField object to manipulate text fields the same way you can with the Properties panel, but instead using script.

  • Define format characteristics for displayed text using the TextFormat object.

  • Use the Key object to determine which keys are being pressed.

  • Ascertain and set the selected area of a text entry field.

  • Use "listeners" to respond (by running a script) any time a specified event occurs.

The countless details of text field instances, formatting, selection, and keyboard access might seem at times to be excessive or just plain confusing. Here's a quick overview of where we're heading so that you won't get too bogged down by details as we step through.

The TextField object is a set of methods and properties (and listeners) to manipulate instances of text onscreen. When authoring, you can create a block of text, set its attributes, and even give it an instance name all by using the Properties panel. The features of the TextField object give you a way to do all that (and more) through script. When you do stuff to a TextField (for example, change its width), you're doing it to one specific instance at a time.

The TextFormat object is comparable to Movie Clips (that have a lot of built-in properties), but an instance of the TextFormat object has properties for all kinds of visual text settings: the color, the font, and the font size. The funky thing about the TextFormat object is that you won't see anything until you apply a TextFormat object (with all its properties) to an individual TextField instance on the Stage. Think of the TextFormat as a style guide. You can apply it to any part of any field.

The Selection object enables you to determine either the portion of a field that's selected or which field has focus (that is, the one currently being accessed). It might seem to be an esoteric and useless detail, but believe me, it's useful. You can use the Selection object to build a really usable data-input screen for your viewers.

The Key object is just a way to determine what the user is doing with his keyboard. In fact, you don't even need any onscreen text to use the Key object for example, consider a game that uses the four arrow keys to control a car's movement.

I find it interesting that the Key object is the only one that really requires the user. That is, you can set a selection, change the format, or modify a field's properties with or without the user. Although you'll write scripts that respond to user interaction, preparing and displaying text often occurs before the user does anything.

graphics/icon02.gif

Finally, this chapter introduces you to another new Flash MX feature: listeners. Several objects (including Key, TextField, and Selection) have specific events that you can cause to trigger a script. Just like how a button has a press event (for which your script can respond), a text field has an onChanged event for which you can trigger a specified script.

The Two "Text" Objects: TextField and TextFormat

As I just explained, there are two similarly named text-related objects, although they have totally different purposes. The TextField object enables you to manipulate properties for any individual text instance. (Only Dynamic and Input text can be affected; Static text has no option to be given an instance name.) The TextFormat object is stored in a variable, and that variable has named properties that you can change (just like how you change a clip's properties). After the variable's properties' values are set the way you want, you can finally apply it to a particular text instance onscreen. You could actually have several variables, each containing a TextFormat object with slightly different values. Then, you could decide to apply only one particular format to the onscreen text. You'll see some examples of this in the section "Using the TextField and TextFormat Objects" later in this chapter.

TextField Object

Basically, the TextField object is a way to control a text instance the way the Properties panel does except by using script. (You can actually do additional things, such as change the background color.) You can give any field on the Stage (Dynamic Text or Input Text) an instance name by using the Properties panel (see Figure 10.1).

Figure 10.1. You can name a text field instance through the Properties panel.

graphics/10fig01.gif

TextField Properties

Once you have a named instance, you can change any property by using the following syntax:

my_txt.property=newValue

my_txt is the text's instance name (_txt is the code hint suffix), and you replace property with the desired TextField property, and newValue with an appropriate value. For example, the following code will set the border property to true:

my_txt.border=true;

This is basically the same as if you had clicked the "Show Border Around Text" option while authoring. The difference here is that you're using script; therefore, the border can appear or disappear any time you specify. In addition, you actually have much more control with script. The "Show Border" option is more appropriately called "Show Border and Background." You can't use the Properties panel to set only the border (with no background). This is just a taste of the way the TextField object lets you do a bit more than the Properties panel alone. (By the way, just use my_txt.background=true to reveal the background as well.)

Before we dive right into a quick rundown of the properties available for the TextField object, an issue about variables is worth discussing. You may be familiar with the way Dynamic or Input Text fields can be associated with a variable. By filling in the "Var:" field in the Properties panel, you effectively link the variable to the onscreen text. That is, if the variable changes, you'll see the onscreen text change, and if the user changes the onscreen text (in the case of input text), he is effectively changing the variable. Although this feature is useful, you'll see that now there is another (arguably better) way. Specifically, any text field has a text property. That is, my_txt.text contains the value currently in the field instance "my_txt." You can change onscreen text by saying my_txt.text="new stuff".

I often still feel attracted to the old way of associating a variable through the "Var:" field. However, I really think it's best to keep your variables as variables, and your text fields as text fields. Just think of them as separate things. There's nothing wrong with using the value of a variable when you assign the text property of a text field (as in my_txt.text=myVar). Realize, however, that associating a variable with a text field will tend to turn that variable's data type to String. For example, a field may look like it contains a 3 when in fact the associate variable's value becomes "3".

Finally, there is one advantage to associating a variable with a text field. Any time the variable changes, the text field automatically updates to reflect the change. Even though I'd recommend avoiding the associated variable feature, its convenience is hard to resist. The thing is, Flash MX has an alternative to associated variables that's even better. It's called a watch. Basically, you specify that you want to watch a particular variable and invoke a function (that you specify) any time it changes. You include code to update the text field's text property inside the function that gets triggered by the variable changes. In fact, a watch can be complex; therefore, I'd say it's okay to use the "Var:" option for simple cases. As you'll see in Chapter 14, "Extending ActionScript," watches can be really powerful as well.

Just understand a text field can have a text property and it can also have a variable associated with it. The two are not the same thing. My recommendation it to try to avoid the "Var:" option to associate a variable.

The form textInstance.property should look strikingly familiar to clip.property or object.property. These are all the same. Once you understand this form, you can explore all the TextField properties (see Figure 10.2) and, for that matter, the methods. Just remember that these are always specific to an instance. That is, you always say "instanceName.property," not "TextField.property," the way you might expect if you compare to objects such as Math or Number. Those objects always include the object name verbatim (as in Math.random()). With the TextField object, you always address an individual text instance.

Figure 10.2. The properties for the TextField object include many that are inaccessible through the Properties panel.

graphics/10fig02.gif

Also, you don't need to instantiate the TextField object; having an instance on the Stage is enough. By the way, the Movie Clip object even has a method to create text fields on-the-fly. It's just like attachMovieClip(), but it's called createTextField(). The syntax is nearly the same:

this.createTextField("my_txt", level, x, y, width, height);

This preceding code creates a new text field with the instance name "my_txt". You just need to replace level, x, y, width, and height with suitable numbers. For example, the following code places a new text field (instance name "my_txt") in level 1 of the this timeline at the top-left corner (0,0) with a width of 300 pixels and height of 100:

this.createTextField("my_txt", 1, 0,0,300,100);

You might not see anything if you place the preceding code in the first keyframe of a movie; however, you can follow the code with this simple script to assign a value to the text property:

my_txt.text="This is new text";

Without carrying this example any further, just realize that once you have a field, you can start affecting it or changing it. It doesn't matter if that field was drawn by hand while authoring or spawned at runtime using the createTextField() method. The text simply needs an instance name that's all.

At this point it should be pretty clear that you can access and set any property of a text field. The following discussion of nearly every property available should be approached rather casually. That is, it's not as though you have to memorize every property. For one thing, that's impractical because there are so many properties (including quite a few more for the TextFormat object). Just try to get a sense for the possibilities; in the case of the TextField object, that's mainly the stuff you do with the Properties panel. When you have a practical objective, you might still need to come back to reference this material or reach for the Flash manuals.

I keep saying that the properties available are like a script version of the Properties panel. In Figure 10.3, I've labeled most of the buttons on the Properties panel with the corresponding script property. You can easily explore each button by drawing some text and experimenting. The idea is that anything you can do by hand during authoring, you also can do with script at runtime.

Figure 10.3. All the standard Text Field properties have equivalent script property names.

graphics/10fig03.gif

Most of these are pretty straightforward; however, I have a few notes. All the properties for any attribute that uses a toggle button (pressed or not pressed) are Boolean (that is, either true or false). For example, my_txt.selectable is either true or false (and, yes, you can set these properties for example my_txt.selectable=false). Interestingly, the embedFonts property is either true or false; it doesn't consider whether you embedded all the font outlines or just some of them. Anything except the default "No Characters" is considered true.

  • type has a string value of either "dynamic" or "input."

  • password, multiLine, and wordWrap are all part of the Line Type drop-down. For example, you could effectively select the "Multiline no wrap" option with two lines of script: multiLine=true; and wordWrap=false;.

  • variable is a property that's always in the form of a string. That is, you can't set a field's variable by saying: my_txt.variable=myVar because the value of myVar would be used. Instead, you could say my_txt.variable="myVar". (Similarly, when you ascertain the variable property, you're given back a string version of the variable name.)

  • textColor is returned in an integer form. You'll learn more about what that means, as well as how to convert such integers to hex or RGB form, in Chapter 12, "Objects."

Wow, the preceding were just the properties that have a direct relationship to the Properties panel. Without going through every remaining detail, I do have a few comments to add, so let's go through them quickly.

  • textHeight and textWidth have nothing to do with font size, but rather the size of the box drawn around the text. If you set background or border to true,you'll see a box with the same width and height.

  • autoSize is pretty cool. Normally, text fields have an autosize of "none" (a string value), which means the margins don't move. If you set autoSize to "center", you see both margins expand (or contract) whenever the field's text changes. It's easiest to understand when you see it. Make an Input Text field called "my_txt" and put this script in frame 1: my_txt.autoSize="center". Then test the movie and type into the field. You can also try autoSize="right" or autoSize="left".

  • background, border, backgroundColor, and borderColor are cool because they let you create settings that are otherwise impossible through the Properties panel. (They're also dynamic, of course, which is why scripting is cool.) Anyway, background and border are independent in that you can have one with or without the other. Changing the color with script accepts either an integer color value or a hex value (more about this in Chapter 12). If you want to use a hex value, just precede it with 0x (and don't use a string). For example, my_txt.backgroundColor=0xFFFF00 makes a yellow background. (Be sure that the my_txt instance either has a background set manually or that its background property is set to true).

  • bottomScroll, maxscroll, and scroll are ways to determine or control which portion of a multiline field is visible. The scroll property actually causes the visible portion to change. Suppose, for example, that your text field is vertically big enough to show just two out of a total of four lines of text. The code my_txt.scroll++ will make the text appear to scroll up; That is, lines 2 and 3 will be visible rather than lines 1 and 2. maxscroll and bottomScroll are read-only. (You can't set them.) maxscroll indicates the highest number scroll will ever reach (that is, the line number of the line at the top when the text is scrolled all the way up). In the case of two lines showing out of a total of four, the maxscroll is three lines because when it's scrolled all the way up (to show lines 3 and 4), line 3 is on top. Similarly, bottomScroll is the line number of the text currently showing in the last line. When the sample two lines showing out of four begins, bottomScroll is 2 because line 2 is sitting at the bottom. scroll might seem to be the most complex property, but it's really simple: It's the line number of the text at the top of the field. Because it's the one you set,it seems more complex.

  • restrict is pretty fancy. You can specify either the characters you want to allow the user to type or the characters you want to prevent the user from typing. You can assign the restrict property to a string that lists several characters (such as my_txt.restrict="abc") or a range (such as my_txt.restrict="a-c"). In either case, the user will be able to type only a, b, or c. If you want to exclude characters, just begin with a caret (^). For example, use my_txt.restrict="^aeiou" to allow all characters except for lowercase vowels. Remember from Chapter 4, "Basic Programming in Flash," that you can use a double-backslash to escape characters. If you want to specify ^, -, or \ as characters that are being included or excluded, you need to use the following sequences instead: \\^, \\-, or \\\\, respectively. (The double backslash means that the second one is to be taken verbatim; the quadruple backslash is really just two double backslashes, which are necessary to escape a backslash.)

Again, don't try to memorize all the properties. Notice, however, that all the properties affect the entire text field instance. It's not so much for making subtle changes to the appearance; rather, the changes affect the entire field in a functional way (such as changing its "type" from "Dynamic" to "Input"). Also, as static properties, they aren't as involved as the methods, events, and listeners you're about to see.

Callback Functions for Events

Before we get to the methods, let me introduce events and listeners. You should be familiar with events by now but only a button's mouse events and a clip's clip events. In the case of both clips and buttons, you place scripts inside an event attached right to the instance. In this section, you'll see how you can trigger events another way: by using callback functions.

Callback functions can take one of two basic forms; in either case, you'll assign a function as the value for an available event. In the first form, you just cram the function definition right where you assign the event's value, as follows:

instance.onEvent=function(){trace("do script");}

In the second form, you define the function first and then point to it by name when assigning the event's value, as follows:

function myFunction(){   trace("doing a script");  }  instance.onEvent=myFunction;

The two forms shouldn't look terribly different. The second form exposes myFunction() so that you can use it in other places; however, it also takes up a bit of memory. Therefore, if you're not using the code anywhere else, the first form is more efficient. In both cases, you have to replace both instance with the button, clip, or text instance name, and onEvent with a legitimate event that's available for that object type. For example, onPress is an event available to only clips and buttons, so the following is legitimate:

myButton.onPress=function(){trace("you pressed")}

This is the same as placing the following code right on a button instance:

on (press){   trace("you pressed");  }

Why haven't I shown you this way to avoid putting code right on button and clip instances? Well, you'll actually learn even more about callback functions in Chapter 14. It's just appropriate in order to learn about the various events and listeners available to the objects discussed in this chapter.

The following events are available to text field instances:

  • onChanged triggers every time the field's text changes.

  • onKillFocus triggers when the text field loses focus (such as when the user tabs out of the field).

  • onScroller triggers every time the field scrolls.

  • onSetFocus triggers when the field receives focus (such as when the user tabs into the field).

It's really pretty simple to specify which function you want to be triggered when any one of these events occurs to a text instance. For example, the following code will trigger the homemade checkit() function every time the contents of an Input Text field called my_txt changes:

1 function checkit(){ 2   trace("checking what you typed...");  3   if(my_txt.text=="flash"){ 4     trace("You typed 'flash'!");  5   }  6 }  7 my_txt.onChanged=checkit;

Although this isn't very practical as is, it shows how you can trigger a function any time the user types into the field. By the way, you can actually change the condition in line 3's if statement from my_txt.text=="flash" to this.text=="flash", because in this case, this is my_txt. This way, you can share checkit() (that is, also trigger it with other text field instances) with the following code:

other_txt.onChanged=checkit;

Although this quick explanation of callback functions is good for a general understanding, there's another confusingly similar feature, called listeners. Before we look at listeners, understand that for any one text field, you can assign only one function per event. (This is also true for clips, buttons, and any other object that accepts callback functions.) For example, if you have another function like checkit() (say checkitAgain()), the following code will override whichever function currently is residing in the other_txt instance's onChanged event:

other_txt.onChanged=checkitAgain;

The point is, just like when you replace the value in a variable, the new function replaces whatever used to be in that event. Saying that "this instance's onChanged event now equals this function" also implies that you mean to wipe away any function that happens to already be present in the onChanged event. Actually, turning off the callback function involves reassigning the event to null. For example, the following code will stop triggering any callback function for the other_txt instance:

other_txt.onChanged=null;

It's not that having one function per event is really that big of a deal. After all, if you want additional scripts to execute, you could just add more code to the function when you define it. However, this approach is not particularly modular and particularly problematic if you want to write code (for a component, for example) that has no chance of breaking other stuff. That is, it's not ideal if your component overwrites an onChanged event that a user had planned on using herself. Again, this scenario is not the end of the world, but it provides a good segue to listeners.

Listeners

Listeners are similar to callback functions except that listeners are easier to turn on and off; in addition, listeners are cumulative in that one text field could have several listeners that all "listen for" the same event. The confusing part is that in the case of text fields, there are four events (to which you can attach a callback function), but there are only two listeners available (onChanged and onScroller). I suppose it's also confusing because they have the same name as two of the events! You sure don't need memorize which one is which (you can see them listed in Figure 10.4), but realize as you discover listeners and events for other objects that you might find apparent inconsistencies and limitations. You can usually find a workaround, but note the differences. For now, we're just going to cover text fields, but remember that there are several other objects that can use their own listeners.

Figure 10.4. Listeners are similar to (and even share names with) callback functions.

graphics/10fig04.gif

Note

Although I haven't covered so called "generic objects" yet, you'll learn everything there is to know throughout the next few chapters. For now, realize they're just variables. Unlike most variables, which contain a single value, generic objects can maintain as many values as you want. Each one is given a name. Whereas a plain variable has a name and a value, a generic object has a name and many named properties each with its own value. You can also compare generic objects to clip instances; the syntax is the same. That is, a clip named "myClip" could have a property called "myProperty" (myClip.myProperty), and the syntax would look the same as the a generic object named "myObject" and it's "myProperty" property (myObject.myProperty). The only difference is that Movie Clips become instantiated when you drag an instance from the Library. Generic objects always need to be instantiated with the code new Object() verbatim. For example, to create a generic object in the variable myObject, use

myObject = new Object();

After completing that step, you can start to add properties using the same syntax as clips. That is, myObject.myProperty="myValue" will store the string "myValue" into the myProperty property of the myObject generic object. You'll hear this explanation several more times throughout the book.

It always takes three general steps to add a listener. (It's cumulative, so you're "adding.") You create a generic object with a named property for the event you want to listen for, assign a function to that event, and then add that generic object as a listener to your text field instance (or to whichever object you're adding a listener).

Here's a simple example that displays a message in the Output window any time the user changes the text in the Input Text field "my_txt":

 1 //define the function that gets triggered   2 function myFunction(){  3   trace("some text changed");   4 }   5 //create a generic object that becomes our listener   6 myListener = new Object();   7 //make an onChanged property point to our function   8 myListener.onChanged=myFunction;   9 //add the listener to the my_txt instance  10 my_txt.addListener(myListener);

Notice that I broke it up so that first the function is defined (lines 2 4), and then the generic object variable myListener has its onChanged property assigned to that function (line 8). I could have crammed the entire function definition into line 8 (as in myListener.onChanged=function(){trace("some text changed");}) and forgone the separate function declaration. Finally, once the function and listener property are fashioned properly, the addListener method identifies which listener will be added to my_txt that is, the addListener's parameter is the generic object myListener in line 10.

Now that we have constructed the listener, we can do a few really interesting things. First, it's easy to share the myListener variable by adding it (as a listener) to other text fields. For example, other_txt.addListener(myListener) will trigger the same function when the field called other_txt is changed. Furthermore, because myListener is an object variable (that is "reference," not "primitive"), if we change its value, we effectively change all the added listeners. For example, myListener.onChanged=function(){trace("new version");} will overwrite the function stored in myListener's onChanged property and, therefore, change both text instances' listeners. (Chapter 4 covers reference and primitive data types.)

In addition to sharing or changing the myListener object, we can add more listeners to an individual text instance (to listen for the same event but trigger different functions). Recall that you can have only one callback function per event for a single text instance. With listeners, you just keep adding as many callback functions as you want. For example, say you have three input fields (firstName, lastName, and phoneNumber) and you want a clip instance (clip) to blink every time the user changes any field. Additionally, you want the clip to rotate any time the number field changes (but you also want the clip to blink). Despite being somewhat impractical, this is a good example to see how easy such a task is when you use listeners. (More practical examples appear later.) Consider that "clip" contains a different graphic in each of its two frames and a stop() in frame 1. Check out the following code, and then I'll explain it:

 1 //make a listener called "playClip"   2 playClip=new Object();   3 playClip.onChanged=function(){clip.play()};   4   5 //make a listener called "rotateClip"   6 rotateClip=new Object();   7 rotateClip.onChanged=function(){clip._rotation+=5};   8   9 //add the play clip listener to all three fields  10 firstName.addListener(playClip);  11 lastName.addListener(playClip);  12 phoneNumber.addListener(playClip);  13  14 //additionally, add the rotateClip listener to number  15 number.addListener(rotateClip);

These two listeners (playClip and rotateClip) have names that refer to the function they trigger rather than the event for which they are listening. Also (in lines 3 and 7), I use the technique of defining the function in one line instead of pointing to a predefined function. The two points to understand in this example are that all three fields share the playClip listener and that the phoneNumber field has an additional listener called rotateClip. Typing in any field makes the clip play and, additionally, typing into the phoneNumber field makes the clip also rotate. If you decide that the phoneNumber field should include only the rotateClip listener and not the playClip listener, you can use the removeListener() method, as follows:

number.removeListener(playClip);

The fact that you can easily share, add, or remove listeners makes them much more convenient than plain callback functions.

TextField Methods

We've covered the TextField object's properties, events, and listeners. The only thing left is its methods. We've seen a couple already (addListener() and removeListener()), but the rest are easiest to understand after you learn about the other objects in this chapter. For example, replaceSel() makes more sense after you see how the Selection object works.

There's one method that will answer the burning question you should have. That is, how do you manipulate other properties, such as fonts, italic, bold, and so on? The TextField object really only showed us how to control attributes of the entire field not subtle formatting issues within the field. The answer is the TextFormat object. As you're about to see, the TextFormat object enables you to set all kinds of formatting properties; however, nothing really happens unless you apply that formatting to a particular text field. Specifically, you use the TextField method called setTextFormat() to apply formatting to all or part of a field. (You can format part of a field differently than the rest.) Before you can use setTextFormat(), you have to fashion a variable (which will contain an instance of the TextFormat object). For example, the following code will cause an entire text field instance (my_txt) to be formatted, but it assumes the myFormat variable has been previously assigned:

my_txt.setTextFormat(myFormat);

There are a couple of more details to the setTextFormat() method (such as how you can provide optional parameters to format only certain characters). Also, there's a setNewTextFormat() method, which specifies how any new text that's about to appear in the field will be formatted much like how you can change the font in your word processor and then begin typing. Simply changing the font (without selecting anything) is like setting the new text format. However, for any of these examples, you must first create a TextFormat variable with the properties you desire so we'll just jump into that next.

TextFormat Object

Luckily, when you explore the properties for the TextFormat object, you'll find familiar terms, such as align, font, italic, and so on (see Figure 10.5). Furthermore, the TextFormat object has only one method and no events or listeners. After all, a variable containing a TextFormat object is nothing more than the data describing a particular style; there's no instance on the Stage to which it's linked.

Figure 10.5. The TextFormat object's properties should look familiar because most are common formatting terms.

graphics/10fig05.gif

Once you learn the TextFormat object's syntax, you'll find it's very easy to use. There are three basic steps: store an instance of the TextFormat object in a variable, modify the named properties of that variable, and then apply the format to some or all of a text field by using the TextField object's setTextFormat() method. Here's an example:

1 myText.text="My name is Phillip Kerman";  2 myFormat = new TextFormat();  3 myFormat.bold=true;  4 myFormat.italic=true;  5 myFormat.underline=true;  6 myText.setTextFormat(myFormat);

Line 1 stores some text in the text field instance called myText. Line 2 creates a variable called myFormat that will contain an instance of the TextFormat object. I use lines 3 5 to individually set the bold, italic, and underline properties to true. Finally, line 6 is necessary in order to see anything onscreen. You can define a format in a variable, but you must take that final step to see anything onscreen.

It turns out that when you call the new TextFormat() constructor function, there a ton of optional parameters nearly one for each property available. Personally, I like doing it the way I showed above even though it takes three lines to set three properties. And because the optional parameters have an expected order (font, size, color, bold, italic, underline, url, target, align, leftMargin, rightMargin, indent, and leading), if you plan to set only bold and italic (the fourth and fifth parameters), you need to make sure to fill in the first three parameters with a value or null. For example, I could modify my earlier example by changing the second line to:

myFormat=new TextFormat(null, null, null, true, true, true)

Sure, I could then skip the next three lines (3 5), but it's so cryptic to have to decode which parameter is which.

By the way, you can change just part of the text field when using setTextFormat(). Consider the following example in which I format my last name differently than the rest of the field:

 1 myText.text="My name is Phillip Kerman";   2 main_fmt = new TextFormat();   3 main_fmt.bold=true;   4 main_fmt.italic=false;   5 main_fmt.underline=true;   6 other_fmt = new TextFormat();   7 other_fmt.bold=false;   8 other_fmt.italic=true;   9 other_fmt.underline=false;  10 myText.setTextFormat(0,10, main_fmt);  11 myText.setTextFormat(11,myText.length, other_fmt);

Notice that I'm creating two format variables: main_fmt and other_fmt. (_fmt is the suffix to trigger TextFormat code hints.) Then, in line 10, I'm using the optional parameters to apply main_fmt only to characters 0 through 10. Finally, in line 11, I apply the other_fmt format to characters 11 through the end (that is the length of the text field's contents). The setTextFormat() can accept a single parameter for format and the entire field gets formatted, or two additional parameters (in the first two slots) for start and end index, or even just one extra parameter to format a single character. In every case, the last parameter is the variable containing the TextFormat object. Finally, I wasn't just trying to show off with the myText.length parameter in line 11 I just got tired of counting characters! (I mean, I could have used something like myText.text.indexOf("Phillip") in place of the 10 and 11 if I wanted to be more dynamic.) In addition, it should jog your memory of the String object. If you're really astute, you'll wonder why I didn't use myText.text.length because, after all, it's the length of the text string in the field that I was trying to reference. This is a logical and totally accurate interpretation. It turns out, however, that text fields also have a length property, which is not only easier to use (less typing), but it performs faster.

There's one important issue to always remember: You can only format text that exists. This may seem obvious, but it's easy to think, "I'll make a field, then I'll format the field, then I'll set the field's text property to populate it with some words." It just won't work in that order.

There really isn't much more to say about the TextFormat object. You'll find some powerful properties, such as tabStops, leading, and bullet. Most are pretty easy to work through, but some require that you learn about arrays and generic objects. For example, you can easily assign the font property to an instance of the TextFormat object but it won't display properly unless the user has that font on her computer (or, if you embed all the font outlines, which adds to the file size). Fortunately, the TextField object has a method called getFontList() that returns an array containing every font installed on the current computer. Once you learn about arrays, you can easily confirm that a desired font is available before you set the font property. I'd suggest revisiting the TextFormat object with the Reference panel after you read the next few chapters or when you have a project at hand. For more practical examples, see the section "Using the TextField and TextFormat Objects" later in this chapter.

HTML Formatting

Just when you thought you learned everything about text formatting, there's actually another way! Provided that your text field has the option selected to "Render text as HTML" in the Property panel (or has its html property set to true, which is the same thing), you can use a limited set of basic HTML tags. This option was shown earlier in Figure 10.3.

Only the following HTML tags are supported: <A>, <B>, <FONT COLOR>, <FONT FACE>, <FONT SIZE>, <I>, <P>, and <U>.

In addition, the following attributes are supported: LEFTMARGIN, RIGHTMARGIN, ALIGN, INDENT, and LEADING.

There's really not too much to it, but I do feel compelled to mention one other detail. In addition to requiring a text field with the html property set to true, you'll want to be sure to assign the value of the text field's htmlText property (not the text property we've been using). For example, only the first example shown here will work:

myText.htmlText="This is <i>cool</i>"  myText.text="This <b>won't</b> work"

If you've got a ton of existing content already in HTML format, it might make sense to use this feature. It is a standard after all. Also, it's sort of nice the way you can intuitively format portions of a string by using the standard tag system. However, the TextFormat object has a simplicity, beauty, and consistency with the rest of ActionScript that makes it desirable. There certainly is some redundancy in that both HTML text and the TextFormat object can do similar things.

Using the TextField and TextFormat Objects

The preceding examples were pretty impractical. While learning, it makes sense to remove unnecessary complexity. The following examples should help you see that the TextField and TextFormat objects have great power. I'll reveal the solutions for several tasks and then walk through the code.

Example: Search and Highlight

In this example, the user will be faced with an Input Text field into which he can type a search term. The code will then highlight (really, format) each word that matches the pattern he types. Only three pieces are onscreen: a large multiline Dynamic Text field (instance name main), a small Input Text field (instance name search), and a button instance called my_btn. (_btn is the code-hint suffix for buttons). Because I want to show how button instances can have callback functions, all the following code (even that for the button) will go inside a keyframe:

 1 //fill the onscreen text   2 main.text="The quick brown fox jumps over the lazy dog.";   3 main.text+="  Then, I take a rest because I'm lazy.";   4 main.text+="  Finally, I mention my dog Max.";   5 main.text+="  He's not brown but he is lazy.";   6   7 //create a "plain" format   8 plainFormat=new TextFormat();   9 plainFormat.underline=false;  10 plainFormat.italic=false;  11 plainFormat.color=0x000000;  12  13 //create a "highlight" format  14 hiFormat=new TextFormat();  15 hiFormat.underline=true;  16 hiFormat.italic=true;  17 hiFormat.color=0xFF0000;  18  19 //define the function that searches  20 function doSearch(){ 21   main.setTextFormat(plainFormat);  22   var pat=search.text.toUpperCase();  23   var allText=main.text.toUpperCase();  24   var found=allText.indexOf(pat);  25   while (found!=-1) { 26      main.setTextFormat(found, found+pat.length, hiFormat);  27      found=allText.indexOf(pat, found+1);  28   }  29 }  30  31 //set the button's callback function to our function  32 my_btn.onPress=doSearch;

If you're a real geek, you should love the fact that this entire script is placed in a single keyframe. Of course, it's possible to just fill the main text field by hand and skip the first 5 lines (which, I broke up into 5 lines for readability). Lines 8 11 create and fashion the plainFormat TextFormat variable. Then, in lines 14 17, I create another similar format variable called hiFormat. (Notice, too, in lines 11 and 17, that I specify a color by using the hex string format preceded by 0x, which tells Flash that I'm specifying a hex value.) Line 32 makes the my_btn button trigger our custom function doSearch whenever the onPress event occurs. (Notice that when pointing to a callback function, you don't invoke the function by saying functionName(), but rather by using functionName with no parentheses.) The doSearch() function is where things get just a bit tricky. In line 21, I just reformat the entire main instance (to clear any formatting that might be left over from the last time the user searched). The next two local variables (pat and allText) are created for convenience. (They're nice, short names that are easier to type and to read when they appear several times later.) Into pat I store the "pattern" that I'm searching for that is, the text of the search field. Into allText I just place the whole main text field. I change both pat and allText to uppercase so that this feature will be case-insensitive. Line 24 does the first search (using indexOf())and places the result (that is the character position where the pattern is found) into the variable found. Then the while loop (lines 25 28) keeps doing two lines of code while found is not equal to -1 (which is the code for when indexOf() finds no match). The first of two lines in the loop (line 26) applies the hiFormat format to certain characters in the field main. Specifically, all the characters from found (where the match was found) through found+pat.length (the found location plus however many characters are in the pattern). Then line 27 sets found for the next match. Notice that the optional second parameter for indexOf() (the "begin index") is provided (found+1) so that the next match is found that is, it starts searching just past where the last match was found.

You can probably find a few tiny issues with this example, including that it doesn't search for whole words. (Try searching for "the," and you'll match the first three characters in "then.") Go ahead and play around with other formatting properties, however, where the plainFormat and hiFormat variables are defined.

Example: Dynamic Roster

For the next example, I wanted to use the createTextField() method (even though it's a Movie Clip method). Basically, the example lets you add names to a list. Actually, every time you enter a new name, a new Input Text field is created and placed on the Stage (underneath the last one). In fact, the basic operation could have been much simpler if I had just concatenated the text in a Dynamic Text field, but this example explores more new concepts from this chapter. All you need is two Input Text fields with instance names firstName and lastName, as well as a button with the instance name add_btn. Then, the following code can go in the first frame:

 1 myFormat=new TextFormat();   2 myFormat.size=38;   3   4 count=0;   5 left=0;   6 top=0;   7 width=500;   8   9 function addOne(){ 10   add_btn._visible=false;  11   count++;  12   var y=top+(count*(myFormat.size+4));  13   var h= myFormat.size+4;  14   var thisOne="field_"+count;  15   _root.createTextField(thisOne, count, left, y, width, h);  16   thisOne=_root[thisOne];  17   thisOne.text=firstName.text + " " + lastName.text;  18   thisOne.type="input";  19   thisOne.border=true;  20   thisOne.setTextFormat(myFormat);  21 }  22  23 add_btn.onPress=addOne;  24 add_btn._visible=false;  25  26 myListener=new Object();  27 myListener.onChanged=function(){ add_btn._visible=true};  28 firstName.addListener(myListener);  29 lastName.addListener(myListener);

The first two lines fashion the myFormat variable that's used for the text that gets created. Then, in lines 4 7, a few variables are initialized (outside of any function so that they get assigned only once). The variable count increments every time a field is created so that we can give each a unique name ("field_1," "field_2," and so on). Also, each new field has to reside in a new level number. The variable left designates the side of each new field (always the same); top is for the starting point for the first field (and then new ones appear spaced below that); width is simply the width of each field. Finally, lines 9 21 define the addOne() function. This is the function that gets triggered by the button add_btn's onPress event (as you can see in line 23).

Let me walk through addOne(). In line 10, add_btn is made invisible (so that a user can't press it twice). Line 11 increments count. Then, in lines 12 and 13, I create two local variables: h and y (for height and vertical location, respectively).

These get used at line 15 in the createTextField() method, which needs values for the vertical location and total height, among other things. It's just easier to read this way. The value for h is based on the format's size (specified earlier in line 2) plus a little bit (to accommodate a lowercase j that appears below the baseline). In line 13, I calculate y by adding to top a space for each line (that is, count*h). Finally, in line 14, I create another local variable (thisOne) that contains a dynamic string based on count so that when I create the text field, I have a unique name. The createTextField() method in line 15 is pretty easy to read with all the previously assigned variables (instead of explicit values). Notice that I must say _root.createTextField() (not this) because the this of this function is the onPress event of the button. Then, in line 16, I convert thisOne into an instance reference by placing the current value (a string) in brackets preceded by the path to that instance. In line 17, I create the text that goes into the field. Next, in lines 18 and 19, I just adjust this newly created text field's properties for type and border. Finally (and now that the field has some text), I set the text field's format to myFormat (defined much earlier).

In line 24, I hide the button right from the start (and in line 10, I hide it every time the user creates a new field). I just thought it would be nice to prevent the user from accidentally creating duplicates. But we need the button to reappear any time the user changes the firstName or lastName fields. A listener is perfect for this. Line 26 initializes the myListener variable and then sets the onChanged property of myListener to a function that sets the button's _visible property to true. Then, in lines 28 and 29, I add this listener to both the firstName and lastName fields. If you had any doubts as to the value of listeners, this example should prove that they're super powerful.

It would be fun to continue with these kinds of examples, but we've got plenty of workshop chapters later in the book and they'll combine more topics than just the TextField and TextFormat objects. Listeners and callback functions appear in a lot of the workshop chapters. The TextField and TextFormat objects appear in Workshop Chapter 13, "Drawing Graphs."

Selection Object

The Selection object enables you to control selectable text fields. Input Text fields are automatically selectable, but you can also set the Selectable option for Dynamic Text fields by using the Properties panel (see Figure 10.6) or by setting the field's selectable property to true. You can control selectable fields in several ways. First, you can set or ascertain which field currently has focus. Only one field can be actively selected at a time, and that field is said to "have focus." In addition to setting or finding out which field has focus, you can also specify (or find out) which portion of that field currently is selected. The most obvious application for this is a situation in which you want to help a user fill out a form. Instead of making the user fill in a blank field, you can preselect a portion so that typing automatically replaces the selected text. Maybe the field says "Enter Name" and instead of making the user select the text (so the user can replace it with his name), you can select it for him.

Figure 10.6. You can turn a Dynamic Text field's selectable properties on or off by using the Properties panel (or by writing a script).

graphics/10fig06.gif

Knowing and setting the selection is also useful when combining the powers of the TextFormat object with the TextField object. For example, you could build a mini word-processor entirely in Flash! You might want to let the user select some text to change its format. You'll need to know which part is selected before you can use setTextFormat() on the field.

Additionally, the Selection object has one event for which you can add a listener: onSetFocus. Any time the focus changes, this listener reports which field just received focus and which one used to have focus. With this, you can determine everything the user is doing and guide him accordingly.

Like the Math object, you never need to instantiate the Selection object. You just use Selection. verbatim for every use: Selection.oneMethod() or Selection.otherMethod(). There aren't multiple instances of "selection" because only one thing can be selected at time. The list of methods for the Selection object is quite short. You can only set or determine which field has focus, set or determine the portion that's selected in a field, and add or remove an onSetFocus listener. We'll look at setting and getting focus first.

Getting and Setting Focus

Selection.getFocus() returns the field name of the currently focused text field. The only catch is that the name returned is in the form of a string (not the actual field instance reference), and it always includes the absolute path to that field. On top of that, the path doesn't start with "_root." but with "_level0." (or _levelx, where x is the level number in which the field resides). Simply finding the name of a field (in string form) might be all you need. And, if you use the following code, you can extract just the field name at the end of the string:

wholeThing=Selection.getFocus();  justVarName=wholeThing.substr(wholeThing.lastIndexOf(".")+1);  //or in one line:  justVarName=Selection.getFocus().substr(Selection.getFocus().lastIndexOf(".")+1);

The fact that getFocus() always returns the string name for your field means that you cannot do something like this:

Selection.getFocus().text="Something new";

Although it might seem logical that you want to replace the text property of the field that currently has focus, getFocus returns a string not a reference to a text instance. Suppose that the currently selected field instance were myText; the preceding code would translate to this meaningless line:

"_level0.myText".text="Something new";

But you intended:

_level0.myText.text="Something new";

The quick solution to this is to use the function eval(). That is, eval("_level0.myText").text="Something new" will assign the string "Something new" to the value of the field itself. Therefore, the following line works as you might expect:

eval(Selection.getFocus()).text="Something new";

The last point regarding getFocus() is that if no field has focus, it will return null.

There is one funky thing about getting focus. If you put the preceding code within a press mouse event, you'd find that regardless of which field had focus, as soon as you click the button, it loses focus! We'll deal with this in the examples, but realize that clicking out of a field causes no field to have focus.

As for the setFocus() method, you must specify a string form of the field you want to set focus to. For example, to set focus on the field instance myText (that resides in the root, not in a clip's timeline), use:

Selection.setFocus("_root.myText");

Notice that the field instance name is in quotation marks. Although getFocus() returns a string form starting with the level number, you can (if you want) use the hierarchy starting with _root (or, for that matter, relative references such as this and _parent). Notice, too, that the preceding method doesn't return a value; rather, it goes ahead and sets the focus.

Setting and Getting Selections

After you're sure which field has focus, you can set (or find out) which portion of the field is selected. Before you use any of the following Selection object methods, make sure that there is a currently focused field (that is, just click on the field while testing the movie). Otherwise, the setSelection() method will have no effect, and the various getSelection() calls will return -1.

The setSelection() method is the easiest method to understand. The form is Selection.setSelection(start,end), where start is the index at which you want the selection to start, and end is the last character that is also selected. (Unlike substr(start,end), which is not inclusive of the last parameter, setSelection() includes all the characters inclusively.) As with all the String object methods, the Selection object methods are zero-based (meaning they start counting the first character as index 0).

To find out which portion of a field is selected, you can use the methods Selection.getBeginIndex() and Selection.getEndIndex(). These two are pretty self-explanatory, but just remember that if no field had focus or no characters are selected, these methods will both return -1 (rather than the index at which the selection starts or ends).

Finally, there's another method called getCaretIndex(), which returns the index in front of the blinking cursor. That is, if you click to start typing at the very beginning of a field, Selection.getCaretIndex() will return 0. You can think of this as the index into which the user will start typing (when she starts typing).

The Selection object might not seem terribly exciting. It comes in handy only when you are controlling selectable text fields often in conjunction with the Key object. Just remember that when using the Selection object, you must first have a focused field before you can select text. And, selecting a field (or finding out which fields are selected) uses a string version of the variable name.

Selection Object Listeners

Adding listeners is new to Flash MX, so naturally, adding listeners to the Selection object is new as well. The process is nearly identical to that of adding listeners to text fields. With text fields, however, you add a listener to any one text field instance at a time. But there are no instances of the Selection object rather only one "selection" at any given time. Therefore, the listeners you're about to see could be considered as being "global" that is, they will listen to any selection regardless of where it might be.

Using the Selection object's onSetFocus listener (there's only one) looks similar to the TextField object's listeners, but it's actually different in two ways. First, you always add the listener to the Selection object (not an individual instance) and, second, the function that gets triggered will automatically receive two parameters (one for the name of the field that used to have focus, and one for the field getting focus). The following is a simple example that reports the name of fields involved any time one gets or loses focus:

1 myListener=new Object();  2 myListener.onSetFocus = report;  3  4 function report(oldFocus, newFocus) { 5   trace("the old focus was " + oldFocus);  6   trace("the new focus is " + newFocus);  7 }  8  9 Selection.addListener(myListener);

Line 1 assigns the myListener variable the value of a generic object so that we can then (in line 2) set the onSetFocus property equal to the forthcoming function, report. (Actually, I was surprised this worked because normally you want to declare your functions before referring to them but it does work and is easier to explain this way.) Anyway, the report() function is different than any you've seen applied to a listener's properties because it expects to receive two parameters. This is just a special arbitrary feature of the Selection object's onSetFocus listener. The first parameter contains the name of the field that used to have focus (or null if there was none). The second parameter is the name of the field getting focus (or, null when no field is getting focus). Finally, line 9 adds the listener myListener (fashioned earlier) to the Selection object. Notice that nowhere in this example do you need to specify a field name. The two trace statements will report information into the Output window regardless of how many fields you have onscreen. Also, when you test the movie, you'll notice the values for oldFocus and newFocus are the long string version names of the instances (that start "_level0") similar to what we saw earlier.

Without walking through a practical example, I can give you an idea how you might put this to use. Say you're building a site that includes a custom calculator that shows a user how much she will save by buying whatever product you're selling. Many compelling sales arguments are based on a return on investment (that is, you buy something, and over time, you'll actually save money). You can build what amounts to a spreadsheet into which the user enters numbers based on her situation. To make the result better than a real spreadsheet, you can limit her entries to ranges that make sense. For example, you might not want any fields to accept numbers above 1000. All you need is a function that checks the value of what the user just entered and warn her if she does something out of the range. For example, you can use the following to replace the report function in the preceding code:

function report(oldFocus, newFocus) {   if(eval(oldFocus).text>1000){     eval(oldFocus).text=1000;    }  }

(Remember that you need the other code to create and add the listener in order for this to work.) The idea is that if the text in oldFocus is greater than 1000,we change it back to 1000. You could add more useful features, such as a clip that appears to tell the user what's going on. Also, notice that the eval() function had to convert the string name for oldFocus into an actual text field instance name. If you want a different range (other than 1000) for other fields, you could create a case statement that checks oldFocus against a long list of different fields, each with a different limit. It turns out that the TextField object's onChanged listener may or may not be more appropriate because you can add a listener to each text field instance. You really can do a lot with this object.

Finally, just to tie the Selection object back to the text fields, there's a method of the TextField object called replaceSel() that replaces the currently selected text with whichever value is passed as a parameter. For example, the following will replace the current selection in the field my_txt with "new stuff":

my_txt.replaceSel("new stuff");

Again, you'll need to have a practical application to really understand much of this material, but the range of possibilities is pretty limitless.

Key Object

The Key object consists of just a few methods, several constants, and a couple of listeners. The methods primarily just enable you to ascertain whether a particular key is currently being pressed (whether it "is down") or, in the case of Caps Lock or Num Lock, whether a key "is toggled." The constants (simply properties that never change) are all associated with particular keys. That is, to check whether a key is pressed, you need to specify which key. Each key is a constant (such as Shift, Left Arrow, Delete, and so on).

Using the Key Object

To use the Key object, use the form Key.isDown(whichKey), which returns either true or false and in which whichKey is either the Key object constant for that key or the "virtual key code" for that key. Virtual key codes are almost identical to ASCII. However, whereas ASCII applies only to numbers and characters that appear in strings, virtual key codes are extended to include other keys, such as Shift and Ctrl. What's really funky is that the virtual key code for an alphanumeric key happens to be the same as that key's uppercase ASCII. Although ASCII distinguishes between uppercase and lowercase, it includes only letters and numbers. Virtual key codes, on the other hand, include all keys but don't recognize any difference between uppercase or lowercase. The ASCII for "A" is 65 (as is the virtual key code for the "A" key). However, the ASCII for "a" is 97, whereas 97 happens to be the virtual key code for the "1" on a keyboard's number pad. You'll see that it's easy to determine any key's ASCII or virtual key code. For a practical example of the isDown() method, consider that the virtual key code for the Tab key happens to be 9. Both of the two following expressions will return true if the user happens to press the Tab key:

Key.isDown(9);  Key.isDown(Key.TAB);

Notice that the form for a Key object constant is Key.THEKEY (where THEKEY is TAB, SHIFT, ENTER, and so on). Figure 10.7 shows a full list of the Key objects. You'll notice that all the constants are in uppercase. The Key object constants (as opposed to virtual key codes) make your code a little bit more readable. However, only a handful of Key object constants are available (the ones you'll probably use most commonly).

Figure 10.7. All the Key object features. (Notice that most are just constants the all-uppercase items.)

graphics/10fig07.gif

An important detail when using a Key object to check whether a key is down is to think about when you're checking. One logical place to check is within a keyDown clip event (see Figure 10.8). That is, when the user presses a key, you can check whether one key or the other is being pressed. You could also use the isDown() method within an enterFrame clip event (where you'd keep checking at all times). It turns out that using a listener is vastly easier and less processor-intensive. In any event, realize that these methods return TRUE or FALSE based on the status of the user's keyboard at the time the script is executed.

Figure 10.8. A script that checks whether a particular key is currently pressed is logically placed within a KeyDown clip event.

graphics/10fig08.gif

The other primary feature of the Key object is the isToggled() method. Similar to isDown(), this method tells you whether the Num Lock or Caps Lock key is currently engaged. For example, each of the following expressions return true when the Caps Lock is on:

Key.isToggled(20);  Key.isToggled(Key.CAPSLOCK);

Because there's no constant for the Num Lock key, you must use the virtual key code (144) to determine whether the Num Lock is on:

Key.isToggled(144);

By the way, you can determine the virtual key code for any key you'd like to trap by using the getCode() method. This method will return the virtual key code of the last key pressed. For example, the following script (attached to a clip instance) will display onscreen the virtual key code for any key pressed:

onClipEvent (keyUp) {   trace (Key.getCode());  }

How do you think I determined that the Caps Lock key was 144? I just used this script, pressed the Caps Lock key, and then saw a 144 display in the Output window.

By the way, there's a similar method that lets you learn what the ASCII value for the last key pressed: Key.getAscii().

Key Object Examples

Let's look at a simple example first and then move on to something more advanced. Suppose that you want to let the user move a Movie Clip around the Stage by pressing the arrow keys on his keyboard. The up arrow key moves the clip up, the down arrow key moves it down, and so forth. You can place the following code on the clip instance:

 1 onClipEvent (keyDown) {  2 var howMuch=2   3   if(Key.isDown(Key.DOWN)){  4     _y+=howMuch;   5   }   6   if(Key.isDown(Key.UP)){  7     _y-=howMuch;   8   }   9   if(Key.isDown(Key.RIGHT)){ 10     _x+=howMuch;  11   }  12   if(Key.isDown(Key.LEFT)){ 13     _x-=howMuch;  14   }  15   updateAfterEvent();  16 }

You'll notice that I used a local variable, howMuch, in line 2 so that if I wanted to change the amount the clip moves for each keypress, I could change it in one place. I threw in updateAfterEvent() at the end so that regardless of the movie's framerate, this script would update the onscreen contents every time a key was pressed. (By the way, updateAfterEvent() only affects key and mouse clip events.) Also, because this clip event waits for a "keyDown," the speed of execution is directly related to the "repeat rate" of the user's keyboard settings. You could change the clip event to enterFrame, in which case the script would check which keys were down as frequently as the framerate (that is, 12 fps would "enter frame" 12 times a second).

You may have noticed if you tried the sample code that you can hold two arrow keys at the same time (for instance, down and right) to make the clip move diagonally. Let's add a feature that makes the clip move twice as fast when both the Shift key is pressed and an arrow key is pressed. Replace the line var howMuch=2 with the following code:

var howMuch;  if (Key.isDown(Key.SHIFT)){   howMuch=4;  }else{   howMuch=2;  }

Notice that in this case I chose to establish howMuch as a local variable in the first line (without assigning it), and then I assigned it a value of 4 or 2 based on whether the Shift key is selected.

That was a pretty easy exercise. A challenging exercise you can try which would involve some major adjustments to this script involves using the left or right arrows to rotate the clip and then using the up and down arrows to make the clip move forward or back. When the clip is rotated 180 degrees, make it move down; when it's rotated 0 degrees, make it move up. The solution lies in considering that every time you move the clip, you really move it some x and some y. It just happens that (based on the rotation) sometimes the x is 0 or negative. All you need are the trigonometry methods of the Math object.

Here's the code:

 1 onClipEvent (keyDown) {  2   var howMuchRotation=5;   3     if(Key.isDown(Key.RIGHT)){  4       this._rotation+=howMuchRotation;   5     }   6     if(Key.isDown(Key.LEFT)){  7       this._rotation-=howMuchRotation;   8     }   9   var howMuch=10;  10   var xChange=howMuch*Math.cos ((Math.PI/180)*_rotation);  11   var yChange=howMuch*Math.sin ((Math.PI/180)*_rotation);  12     if (Key.isDown(Key.UP)){ 13       this._x+=xChange;  14       this._y+=yChange;  15     }  16     if (Key.isDown(Key.DOWN)){ 17       this._x-=xChange;  18       this._y-=yChange;  19     }  20   updateAfterEvent();  21 }

Most of this is fairly straightforward, except lines 10 and 11 where I assign the value for xChange and yChange, respectively (the variables used to change the _x and _y positions of the clip). Basically, both Math.sin() and Math.cos() return a number between -1 and 1. The cosine of 0 degrees is 1; the cosine of 180 degrees is -1. Any midpoint is a fraction thereof. The cosine of 90 degrees (halfway between 0 and 180) happens to equal 0. The Movie Clip has a shape that appears to be pointing to the right (refer to Figure 10.8). If it's rotated 180 degrees (pointing left), I want the change in _x position (when moving "forward") to be -1*5 (5 being "how much" that is, the full amount in the negative direction). If the rotation stays at 0 degrees, I want the change in _x to be 1*5 (so that it moves to the right). When it's 90 degrees, I don't want to change the _x at all, so 0*5 gives me zero. I can easily determine the factor to multiply howMuch by (between 1 and -1) just by calculating the cosine of the current rotation. If the rotation is less than 180 degrees, cosine will return a fraction of 1, which causes howMuch to be multiplied by a smaller number (meaning, the change in _x is proportionally less). The only hairy part is that you have to translate _rotation from its value in degrees to the equivalent value in radians (because Flash's Math object uses radians). As you recall from Chapter 5, "Programming Structures," you can translate degrees into radians by multiplying by (Math.PI/180). The change in _y is calculated the same way, but with sine. If the user presses the down arrow, I reduce the values for _x and _y instead of adding to them as is the case with the up arrow. This explanation should give you an idea of what went into the solution even if you can't immediately understand the code. Realize that you're coming in after the code is written, and in reality, you'll need to write the code from the ground up so try to first just get the gist.

There are so many more things we could do by combining all the objects covered in this chapter. Instead of doing any more examples, however, let me just remind you that you can also use listeners with the Key object. For example, the following code serves as a substitute to the virtual key code gathering script shown earlier:

myListener=new Object();  myListener.onKeyUp=function(){trace(Key.getCode())}  Key.addListener(myListener);

It's a lot like using the Selection object's listeners you don't add the listener to a particular key, but to the Key object. For a nice exercise, try converting either of the preceding two examples to use listeners instead. Basically, you won't have to put any code on the clips themselves. (I have my solution to this exercise, as well as many others, at the companion site for this book: www.phillipkerman.com/actionscripting)

Summary

Wow, I think this chapter might be the most fun so far because not only did we explore new concepts, they were easy to apply using our existing base knowledge. You got to see four unique Flash objects: TextField, TextFormat, Selection, and Key. It turns out that they share some similarities, but they're all unique objects.

The material in this chapter is a good follow-up to the String object from last chapter. You should notice that many String object methods came in handy. We got to create, modify, and populate text fields (as well as format the text they display). The Selection object was all about finding or setting focus, and finding or setting the portion of a selectable field that currently is selected. Finally, the Key object enables you to find out which key (or keys) is pressed at any given time.

You know this is fun when you can think up your own exercises or you're so excited to go try this stuff out for your own projects. (Hopefully, you're feeling like that now.) I know I'd go play (with Flash MX) right now if I didn't have the rest of the book to write!

CONTENTS


ActionScripting in MacromediaR FlashT MX
ActionScripting in MacromediaR FlashT MX
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 41

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net