Book HomeMastering Perl/TkSearch this book

8.5. Text Tags

Text tags give you another way to address portions of text in the Text widget. A tag has three purposes, and the same tag can serve all three or only one:

Tags can change how the text appears on the screen; font, size, coloring, and spacing are a few of the text properties affected by tags. You change text properties by creating your own tags (with their own names) and using option/value pairs to assign formatting information. In addition to changing the formatting, you can use a tag to apply a specific binding (such as perform a task when the user double-clicks on that text). A special tag, "sel", manages the selected text. Any time the user selects some text, the location of that text is marked with the "sel" tag.

Any of the text within the Text widget can have one or more tags associated with it. If you apply two tags to the same piece of text and they both alter the font, the last tag applied wins.

8.5.1. Tag Options

The options you can use to configure tagged text are mostly a subset of the configuration options of the Text widget itself. The configuration options for the Text widget are the defaults for all text in the Text widget. Using a tag allows you to change that formatting or binding on a case-by-case basis. There are some options that can be used only through tagged text.

-background => color
Sets the color of the area behind the text.

-bgstipple => pattern
Sets the pattern used to draw the area behind the text. Can create a shaded look.

-borderwidth => amount
Sets the width of the relief drawn around the edges of the text, line by line.

-data => scalar
Associates scalar with a tag, which allows you to store any amount of information for the tag. It can, of course, be a reference.

-elide => boolean
If boolean is true, text covered by the tag is not displayed.

-fgstipple => pattern
Sets the pattern used to draw the text.

-font => fontname
Sets the font used for the text.

-foreground => color
Sets the color of the text.

-justify => 'left' | 'right' | 'center'
Sets the position of the text within the Text widget.

-lmargin1=> amount
Sets the amount of indentation from the left edge for the first line of a paragraph.

-lmargin2=> amount
Sets the amount of indentation from the left edge for the second and greater lines of a paragraph. Sometimes called a hanging indent.

-offset => amount
Sets the amount the text is raised or lowered from the baseline. Can be used to create superscripts and subscripts.

-overstrike => 0 | 1
If a true value, causes the text to have a line drawn through it.

-relief => 'flat' | 'groove' | 'raised' | 'ridge' | 'sunken'
Determines the way the edges of the text are drawn, line by line.

-rmargin => amount
Sets the amount of space left between the text and the right edge of the widget.

-spacing1 => amount
Sets the amount of additional space left on top of a line of text that begins on its own line. Default is 0.

-spacing2 => amount
Sets the amount of additional space left on top of a line of text after it has been wrapped around automatically by the Text widget. Default is 0.

-spacing3 => amount
Sets the amount of additional space left after a line of text has been ended by a "\n". Default is 0.

-state =>'normal' | 'hidden'
Text is normally visible, but you may hide it if desired.

-tabs => list
Indicates the set of tab stops for this text. See Section 8.2.5, "Tab Stops" earlier in this chapter for more detailed information.

-underline => boolean
Indicates that the text should be drawn with an underline.

-wrap => 'none' | 'char' | 'word'
Determines the mode in which the text is wrapped. 'none' means lines that are longer than the Text widget is wide are not wrapped. 'char' will wrap at each character. 'word' will wrap between words.

8.5.2. A Simple Tag Example

Let's look at an example of how a simple tag is created and use it to insert some text into a Text widget (the resulting screen is shown in Figure 8-4):

$t = $mw->Text()->pack( );
$t->tagConfigure('bold', -font => "Courier 24 bold");
$t->insert('end', "This is some normal text\n");
$t->insert('end', "This is some bold text\n", 'bold');

Line 1 creates the Text widget and places it on the screen.

Line 2 creates the 'bold' tag. Don't be fooled by the use of the word "configure" instead of "create." When you configure a tag, you are creating it. We created a tag named 'bold' and associated a different font with it (it happens to be the same as our Unix Text widget default font, just the bold version).

At this point, we haven't changed anything in the Text widget. We are just setting up to use the tag later in the code. You can use any name to indicate a tag as long as it is a valid text string. We could have named the tag "bold_font" or "big_bold_font" or "tag1". If you have good programming style (and want to be able to maintain your code), use a name that indicates what the tag does.

Line 3 inserts some text into the Text widget using the default formatting.

Line 4 inserts some more text into the Text widget but uses the 'bold' tag. The insert method allows us to specify a tag as the third argument. This causes that string of text to be inserted into the Text widget and assigned the tag 'bold'. The 'bold' tag was configured to change the font, so any text with the 'bold' tag will be shown with the different font.

Figure 8-4

Figure 8-4. Text widget with normal and bold text

This is a pretty simplified example. What if we want to alter text that has been typed in by the user? We can't use the insert method then. We use the tagAdd method specifying a range of indexes to apply the tag to:

$t->tagAdd('bold', '1.0', 'end');

This applies the 'bold' tag to all the text within the Text widget.

8.5.3. Selections in a Text Widget Using the "sel" Tag

The "sel" tag is a special tag maintained by the Text widget. Any text the user selects will be assigned the "sel" tag. Unfortunately, there are no easy methods provided that help you determine if there is a selection or where it's located. Here are a few of the basic things you'll want to do with the selection.

To determine if the selection exists:

$if ($t->tagRanges('sel')) {
   ... do something with sel as an index ...
}

You may want to force the selection programmatically by using some of the tag methods (which we haven't covered yet) to put the "sel" tag on some text. For instance, to add the third line to the selected text:

$t->tagAdd('sel', '3.0', '3.0 lineend');

You can have multiple selections in the Text widget, and each time you call tagAdd, you're adding to the selection.

Here's an example that shows how to add another tag to the currently selected text:

$t->tagAdd('bold', 'sel.first', 'sel.last') if ($t->tagRanges('sel'));

When you use the "sel" tag as part of an index, you need to make sure the tag exists (using tagRanges) within the Text widget first or you'll get a really nasty error.

Here are two ways to get the currently selected text:

# Harder way:
$s1 = $t->get('sel.first', 'sel.last') if ($t->tagRanges('sel'));
# Easier way ($s2 is always set to something):
$s2 = $t->getSelected( );

The getSelected method is a convenience method for the Perl/Tk version of the Text widget. Look towards the end of this chapter in Section 8.19, "The Perl/Tk Text Widget Extended Methods" for more selection convenience methods.

8.5.4. Configuring and Creating Tags

The first thing you'll do with a tag is create it by using tagConfigure (unless you're using the automatically defined "sel" tag). The first argument to tagConfigure is the name of the tag. The rest of the arguments (which are optional) are option/value pairs, as described earlier in Section 8.5.1, "Tag Options". Here are some examples:

# creating a tag with no options
$text->tagConfigure("special");
# Creating a tag that will change the color
$text->tagConfigure("blue", -foreground => "blue");
# Creating a tag that will make underlined text
$text->tagConfigure("underline", -underline => 1);
# Creating a tag that changes the color and spacing
$text->tagConfigure("bigblue", -foreground => "blue", -spacing2 => 6);

You can change the settings for an already created tag by using tagConfigure a second time. Any changes you make to the tag immediately affect any text on the screen that has that tag:

# Add background color to "blue" tag
$text->tagConfigure("blue", -background => "red");
# Change the spacing for "bigblue"
$text->tagConfigure("bigblue", -spacing2 => 12);

As with widget configure methods, you can use tagConfigure to find the current settings for a specific tag. To get all the tag options and their values in a list of lists:

@listoflists = $text->tagConfigure("blue");
foreach $l (@list) { print "@$l\n"; }  # print it out

Each list within the list contains two elements: the option name and the value. You can limit the information you retrieve to a single option:

($option, $value) = $text->tagConfigure("blue", -font);

If you only want information on the value for a particular option, use tagCget:

$value = $text->tagCget("bigblue", -spacing2)

8.5.5. Adding a Tag to Existing Text

We've already seen an example of using the tagAdd method. It allows you to add a tag to portions of text in the Text widget. The usage of tagAdd is as follows:

$text->tagAdd('tagname', index1 [ , index2, index1, index2, ... ] )

You can add a tag to a single index or a range of indexes. This means you can add a tag to multiple places in the Text widget at the same time. Let's say you wanted to add the tag 'heading' to the 1st, 12th, and 30th lines because they are the location of some heading information that you want to look different than the rest of the text. The tagAdd line would look like this:

$text->tagAdd('heading', '1.0',  '1.0 lineend', 
                         '12.0', '12.0 lineend', 
                         '30.0', '30.0 lineend');

Now, assuming the formatting of 'heading' makes the font bigger, those lines show up differently than the defaults from the rest of the text in the widget.

You can add more than one tag to a section of text. For example, you can have both a 'heading' tag and a 'color' tag. If both tags try to alter the same option (such as -font), the last setting for that option wins.

Once you place a tag on a range of text, any text inserted between the beginning and ending indexes of that text will automatically get the tag of the characters surrounding it. This happens whether you are using insert without any specific tags or the user just types text into the Text widget. If you specify a tag with insert, it overrides the surrounding tags.

8.5.6. Using bind with Tags

One of the main reasons for tags is the ability to assign a binding to certain portions of the text. After creating a tag with tagConfigure, you can use bind so a callback will execute when a sequence of events happens (such as a mouseclick) on that tagged text. On our Button widgets, we have a default binding of <Button-1> that invokes the callback associated with the -command option. We can do the same thing with tagged text.

The best example is using text like a web hyperlink. When you click on the link, something happens: a new document is loaded, or another window is created and presented to the user. The basic form of a tagBind call is as follows:

$text->tagBind(tagname [, sequence, callback ] )

The callback is similar to that specified for the -command callback on a Button. The sequence is a description of the event that triggers the script. The only sequences you can specify are those that are keyboard or mouse related. (See Chapter 15, "Anatomy of the MainLoop" for more details on available events.)

The following code shows a psuedo-link example. All the link does when we click on it is show the end of the Text widget:

$t = $mw->Scrolled("Text", -width => 40)->pack(-expand => 1, 
                                               -fill => 'both');
$t->tagConfigure('goto_end', -underline => 1, -foreground => 'red');
$t->tagBind('goto_end', "<Button-1>", sub { shift->see('end'); } );

# Setup Bindings to change cursor when over that line
$t->tagBind('goto_end', "<Any-Enter>", 
             sub { shift->configure(-cursor => 'hand2') });
$t->tagBind('goto_end', "<Any-Leave>", 
             sub { shift->configure(-cursor => 'xterm') });
$t->insert('end', "END\n", "goto_end");

# Insert a bunch of lines
for ($i = 1; $i <= 100; $i++) {
  $t->insert('end', "$i\n");
}

Inside the subs in the tagBind calls, we use the shift command to invoke a method. We can do this because the first argument sent to the bind callback is the Text widget. This is done implicitly for you. Whichever widget tagBind is invoked on is the widget that will be sent as the first argument to the callback. To use the Text widget more than once in the callback, assign it to a lexical variable; for example, my $widget = shift.

If we created our Text widget in the global scope of the program and placed a reference to the widget in the variable $t, we could also access the Text widget in the callback via the $t variable. This is only possible because $t is in the global scope and available during the callback. If you have two different Text widgets with which you want to use the same callback, use shift to get the correct Text widget:

$t1->tagBind('goto_end', "<Button-1>", \&goto_end );
$t2->tagBind('goto_end', "<Button-1>", \&goto_end );
sub goto_end {
  my $text = shift;
  $text->see('end');
}

Using the same callback for both Text widgets helps save space in your program.

To determine the bindings for a tagname, use tagBind with the tag name argument only:

@bindings = $text->tagBind("tagname");

The list will be empty if there are no bindings currently for that tag.

8.5.6.1. The Perl/Tk Text widget extended bindings

These bindings are particular to the Perl/Tk Text widget and do not exist in Tcl/Tk. The class methods are explained in Section 8.19, "The Perl/Tk Text Widget Extended Methods".

<F1>
Invokes the clipboardColumnCopy method.

<F2>
Invokes the clipboardColumnCut method.

<F3>
Invokes the clipboardColumnPaste method.

<Insert>
Invokes the ToggleInsertMode method.

<3>
Invokes the PostPopupMenu method.

8.5.7. Deleting All Instances of a Tag

Once a tag is created, you can use the tagDelete method to delete the tag:

$text->tagDelete(tagname [ , tagname ... ])

The tags are deleted completely when you use tagDelete. This means the text reverts back to the default configuration values, and any bindings or other information associated with those tags is also deleted.

The tagDelete method can be used if you are creating temporary tags dynamically within the program and need to delete the tags later when the information is no longer valid.

8.5.8. Removing a Tag from the Text

To remove the tag from a specific block of text, you can use the tagRemove method:

$text->tagRemove(tagname, index1 [, index2, index1, index2 ...])

Specify the name of the tag and an index or range of indexes from which to remove the tag. This leaves the tag intact; it merely removes it from the specific text indicated with the indexes.

8.5.9. Raising and Lowering Tags

When there are several tags applied to the same text, the last tag added to the text overrides the previous ones, and its configuration options are given priority. You can change the priority of the tags by using tagLower and tagRaise:

$text->tagLower(tagname [, belowtag ])
$text->tagRaise(tagname [ , abovetag ])

These methods take a tag name as the first argument. If there is no second tag argument, the first tag is given the highest or lowest priority. This affects the entire text in the Text widget, no matter where the tags are applied. If a second tag is specified, the first tag is placed specifically before or after the second tag.

Think of it as reordering a stack of tags (all applied to the same text). The tag on the top has the most say, and if it has a -foreground option of 'red', all the text with that tag will be red, regardless of what the other text tags set -foreground to. If we use tagRaise to move a tag with -foreground of 'blue' to the top, the tagged text will change to blue.

8.5.10. Getting Tag Names

You can find all the different tags that apply to a specific index, or to the whole Text widget, by using the tagNames method:

$text->tagNames([ index ])

If you specify an index, the list returned contains tags that apply only to that index. If a specific index isn't given, the list returned contains all the tags that apply to the entire Text widget whether or not that tag has been applied to text within the widget.

8.5.11. Determining Where a Tag Applies

If you know the name of the tag, you can find where it applies in the Text widget by using the range methods. The first method, tagRanges, returns a list that contains pairs of index values for the whole Text widget:

@list = $text->tagRanges("tagname")
# returns ( begin1, end1, begin2, end2 ... )

If no text in the Text widget has that tag, the returned list will be empty.

You can use the tagNextrange method to get the pairs of index values one at a time:

($start, $end) = $text->tagNextrange("tagname", index1 [ , index2 ])

The search for "tagname" will begin at index1 and go no farther than index2. If index2 is not specified, the search will continue until the end of the Text widget or until it finds the tagname, whichever comes first.



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.