站内搜索: 请输入搜索关键词
当前页面: 图书首页 > SWT: The Standard Widget Toolkit

7.3 Class Text - SWT: The Standard Widget Toolkit

Previous Section  < Day Day Up >  Next Section

7.3 Class Text

7.3.1 Example

graphics/07inf06.gif

7.3.2 Text Hierarchy

graphics/07inf07.gif

7.3.3 Text Styles

Style

Description

SWT.SINGLE

Allow a single line to be edited

SWT.MULTI

Allow multiple lines to be edited

SWT.READ_ONLY

Make the control noneditable

SWT.WRAP

Allow strings to wrap instead of scrolling

SWT.LEFT

Left-align the contents of the control

SWT.CENTER

Center-align the contents of the control

SWT.RIGHT

Right-align the contents of the control


7.3.4 Text Events

Event

Description

SWT.DefaultSelection

Default selection occurred (user pressed <Enter>)

SWT.Modify

Text has changed in the control

SWT.Verify

Text is to be validated in the control


Text controls are common user interface elements that allow the user to edit strings. Text controls are selectable. The selection in a text control is the insertion point for characters. If the selection extends over one or more characters, the text control indicates this by drawing the selected characters in a different manner than the unselected characters. If the selection does not extend over any characters, it is indicated by a caret (also called an I-beam). A caret is a thin vertical line that appears between characters. The caret provides a visual cue for the user, indicating where keystrokes will go. On some platforms, the caret blinks to get attention.

Text controls support only "plain" text. This means that all characters in the control are the same font and color. Applications that need a more flexible text editing control can use org.eclipse.swt.custom.StyledText, which was designed for use by Eclipse. Note that StyledText is not a native widget.

It is common for an application program to detect when a text control has changed or to filter characters as they are typed. For example, a program may want to prompt the user to save changes when the shell is closed or ensure that only digits can be entered. To do this, programs listen for the SWT.Modify and SWT.Verify events, described later in this section.

There are two kinds of text controls: those that can contain only a single line of text and those that can contain multiple lines of text.

7.3.5 Single-Line and Multiline Text Controls

The SWT.SINGLE style is used to create single-line text controls. The following code fragment creates a single-line text control with a border, then sets the contents of the control to the string "Texan".






Text text = new Text(parent, SWT.SINGLE | SWT.BORDER);

text.setText("Texan");


Multiline text controls are created with the style SWT.MULTI. Unlike their single-line counterparts, they can include scroll bars. Scroll bars are created when you specify the SWT.H_SCROLL or SWT.V_SCROLL styles. The following code fragment creates a multiline text control with a border and scroll bars.






int style =

    SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL;

Text text = new Text(parent, style);


In the following sections, some concepts apply to one or both kinds of text controls. When a concept applies to only one type of text control, this is indicated in the discussion.

7.3.6 String Operations

Text controls support setting and getting their text in the same manner as Button, Label, and many other widgets in SWT.

setText(String string) Sets the contents of the text control to the specified string. The previous contents are cleared, the new string inserted, and the I-beam is placed just before the first character. If the text control has the style SWT.SINGLE and the string contains line delimiters, the behavior is undefined. For more information about line delimiters, see Line Delimiters in this section. Mnemonic characters are not supported in Text controls.

getText() Returns a string that is the contents of the text control. If the string has never been set, an empty string is returned.

getText(int start, int end) Returns a substring of the text that is contained in the text control. The substring includes characters in the range from start to end, including both the start and end indices. Indexing starts from zero. If the indices are out of range, as many characters as possible that are within the range are returned.

Indexing and Range Operations in SWT

To be consistent with the indexing model used by Java, indexing operations in SWT start from zero. Unfortunately, ranges in SWT include both the start and the end of the interval and do not follow the Java standard. For example, Text.getText(int, int) includes the last character, whereas substring(int, int) does not. This was simply a mistake on our part.

Ranges in SWT are consistent but different from the Java standard. Unfortunately, we can't fix this problem due to backward compatibility issues, even though the Java standard is arguably better (it admits the possibility of an empty range). At this point, any new API in SWT will also have to follow the SWT range convention. The issue here is that we believe consistency to be more important than being "correct" in some places and "wrong" in others. Some people don't agree with this philosophy, but nonetheless, it is one of the fundamental SWT design decisions.


getCharCount() Returns the number of characters in the text control. It is much faster to use this method than to use getText() to get the contents, then ask the resulting string for its length().

7.3.7 Passwords and the Echo Character

Single-line text controls can be used to obtain a password from the user. As the user types, a specific character (called the echo character) is displayed instead of the one that was typed. The SWT.PASSWORD style is used to create text controls that display in this fashion. The following code fragment uses SWT.PASSWORD to create a single-line text control capable of acquiring a password from the user.






int style = SWT.SINGLE | SWT.BORDER | SWT.PASSWORD;

Text text = new Text(parent, style);

text.setText("fred54"); //this text won't be displayed


Note that the echo character is platform-specific and can change between themes and even between releases of the operating system.[9] To force the echo character to be the same on every platform, the setEchoChar() method can be used.

[9] This is exactly what happened between releases of Windows 2000 and XP. The asterisk character, echoed on Windows 2000, was changed to a large dot on Windows XP.

setEchoChar(char echo) Sets the character that will be displayed as the user types. If the echo character is '\0', the control no longer hides characters, and the current text is displayed.

getEchoChar() Returns the character that was set by setEchoChar(). If an echo character was not set, '\0' is returned, indicating that the control is drawing characters normally. When the SWT.PASSWORD style is used, the result of getEchoChar() is undefined.

Generally speaking, it is unwise to set the echo character because it is part of the platform look and feel.

7.3.8 Lines and Line Height

You can query the number of lines in a text control and the height of each line using getLineCount() and getLineHeight().

getLineCount() Returns the number of lines in the control. If the control is a single-line text control, the integer 1 is returned.

getLineHeight() Returns the height of a line in the control in pixels. This value can be different from the height of the font that is in use by the control because some platforms include extra space between lines.

The getLineHeight() method is used mainly when computing the initial size of a text control. The following program uses getLineHeight() and FontMetrics (see Class FontMetrics in the Fonts chapter) to set the size of a multiline text control so that it displays about five rows of text and is about ten characters wide.






public static void main(String[] args) {

    Display display = new Display();

    Shell shell = new Shell(display);

    Text text = new Text(shell, SWT.H_SCROLL|SWT.V_SCROLL);

    int rows = 5, columns = 10;

    GC gc = new GC(text);

    FontMetrics fm = gc.getFontMetrics();

    gc.dispose();

    int height = rows * text.getLineHeight();

    int width = columns * fm.getAverageCharWidth();

    text.setSize(text.computeSize (width, height));

    shell.pack();

    shell.open();

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    display.dispose();

  }


Figure 7.2 shows the result of running this example and typing three lines of text into the control.

Figure 7.2.

graphics/07fig02.gif


7.3.9 Line Delimiters

Different operating systems use different line delimiters to separate lines in multiline text controls.[10] Because SWT uses the native text control and cannot change the delimiter that it uses, strings are processed by SWT text controls using these platform-specific line delimiters.[11]

[10] Note that we are talking about the delimiters used by text controls here: On Windows, the line delimiter is "\r\n". On X/Motif and GTK, the delimiter is "\n". On the Macintosh, it is "\r". This should not be confused with the line delimiter used when storing text files on disk, which may be different, as it is on Mac OS X.

[11] This was a necessity. Because the delimiters can be different lengths, attempting to convert the delimiters automatically would have required either continuous linear scans of the contents or impossible amounts of internal bookkeeping.

Using the line delimiter for one platform when running on another can cause portability problems. To ensure that you always use the right delimiter, you can use the constant Text.DELIMITER or the method getLineDelimiter().

getLineDelimiter() Returns the line delimiter for the text control.

The following code fragment sets the contents of a multiline Text control to contain three lines of text.






String lf = Text.DELIMITER;

text.setText("Line 1" + lf + "Line 2" + lf + "Line 3");


As a convenience, multiline text controls can also accept text that is delimited using "\n" on every platform. This means that using the text control line delimiter is not absolutely necessary when setting text into a text control. However, when text is queried back, the string that is returned uses the platform delimiter. Thus, making use of this convenience is problematic for all but the simplest uses of the control. For example, if your code computes string offsets in a string delimited by "\n", these offsets will not match character positions in the text control on Microsoft Windows. The best way to avoid this kind of error is to ensure that the source string also uses the operating system delimiter. Because the text control will accept "\n" as a delimiter, one possible way to convert the string is using setText() and getText(), like the following.






String string = "Line 1\nLine 2\nLine 3\nLine 4";

text.setText(string);

string = text.getText();


7.3.10 The Selection

Substrings within a text control can be selected using the setSelection() method. This method takes a selection range. Indices are zero-based and describe a range of integer selection positions anywhere from 0 to N, where N is the number of characters in the control.

At first glance, it might seem that including the Nth "character" in the selection range is an error. However, this makes sense because selection indices address caret positions, not characters. Caret positions occur between characters. Given N characters, there are N+1 possible caret positions. To address N+1 positions, the indices 0 to N are needed.

Figure 7.3 shows the caret positions in a string that is five characters long:

Figure 7.3. Caret positions in a string within a text control.

graphics/07fig03.gif


When the start of the selection is the same as the end, the selection range is empty, and an I-beam is displayed.

setSelection(int start, int end) Sets the selection in the text control. All characters between the start and end caret positions are selected. When start and end are the same, the selection becomes an I-beam. If any of the indices are out of range, as much text as possible that is within the bounds of the text is selected. The text control is scrolled to ensure that the user sees the new selection.

setSelection(int start) Sets the selection in the text control. This method is the same as calling setSelection(start, start).

setSelection(Point selection) Sets the selection in a text control. This method is the same as calling setSelection(selection.x, selection.y).

Why Is Point Used to Represent the Selection in a Text Control?

The earliest versions of SWT represented selection in a text control using an integer array. Subsequently, it was decided that the integer array was too confusing, and a Range class was introduced. The Range class had start and end fields. Then it was decided that the Range class added little value because it provided only two fields and had no operations. Because one of the design decisions of SWT is to minimize the number of classes, the Range class was removed and Point was used instead. We admit it's a bit confusing (but not harmful).


selectAll() Selects all the text in the text control. This method is the same as calling setSelection(0, text.getCharCount()) but is slightly faster because it avoids getting the number of characters in the control.

clearSelection() Clears the selection, causing it to draw as an I-beam.

Is the clearSelection() Method Necessary?

It seems on the surface that setting the start of the selection equal to the end should be equivalent to calling clearSelection(). The problem with this is determining where the I-beam should be placed. There are two possibilities: either the start or the end of the previous selection. It turns out that choosing one over the other is not straightforward. Among other factors, the position of the I-beam within the selection depends on the direction that the user has selected text with the mouse. For example, when the user selects from left to right, the I-beam is placed at the end of the selection. If the user selects from right to left, the I-beam is at the start. Using clearSelection() ensures that the I-beam does not move when the selection is cleared, making it a necessary API for text controls.


getSelection() Returns a point that represents the selection. The x coordinate is the start index, and the y coordinate is the end index of the selection range.

The following code fragment uses setSelection(int, int) to select the word There in a text control.






text.setText("Hello There Fred!");

text.setSelection(6, 11);


The following program creates a shell and a text control and initializes it, then uses setSelection(int, int) to select the first occurrence of the string "word" in the control. Figure 7.4 shows the result of running this example.






public static void main(String[] args) {

    Display display = new Display();

    Shell shell = new Shell(display);

    shell.setLayout(new FillLayout());

    int style = SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER;

    Text text = new Text(shell, style);

    text.setText("Here is\na word that\nneeds selecting.");

    shell.setSize(200, 200);

    shell.open();

    String string = "word";

    int index = text.getText().indexOf(string);

    if (index != -1) {

        text.setSelection(index, index + string.length());

    }

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    display.dispose();

}


Figure 7.4. Selecting a word in a text control.

graphics/07fig04.gif


7.3.11 Inserting and Appending

Characters are inserted into a text control using the insert() method. Insertion always happens at the selection. It is not possible to insert text at an arbitrary location within a text control without first moving the selection to the new location.[12]

[12] This fact of life was dictated by the native Windows text control that does not provide API to insert characters anywhere else.

If the selection is an I-beam, the new text is inserted, as you would expect. If the selection is a range, the characters within the range are deleted, making the selection an I-beam and inserting the new characters. If you think about it, the insert() method behaves as though the characters were typed into the control.

insert(String string) Inserts a string into the text control. The new string replaces the current selection. The control is scrolled to show the new string, and the I-beam is placed at the end of the string.

The following code fragment first selects There, then replaces it with the string "New Text".






text.setText("Hello There Fred!");

text.setSelection(6, 11);

text.insert("New Text");


Inserting a string at the end of a text control is very a common operation. For this reason, the convenience operation append() is provided.

append(String string) Inserts a string at the end of the text control. The control is scrolled to show the new string, and the I-beam is placed at the end of the control.

Appending a string is equivalent to setting the selection to the end of the control, inserting the new string, then setting the selection to the end once more. The following code fragment uses append() to append the string "New Text". It then performs the equivalent operation with the string "More New Text" using the methods setSelection() and insert().






text.append("New Text");

text.setSelection (text.getCharCount());

text.insert("More new text");

text.setSelection(text.getCharCount());


7.3.12 Wrapping

When a multiline text control is created with the SWT.WRAP style, it will wrap any line that cannot be fully displayed in the control.

If you provide both the SWT.WRAP and SWT.H_SCROLL styles, SWT.H_SCROLL is ignored (i.e., the control will not create a horizontal scroll bar). A horizontal scroll bar is used to allow text lines that are wider than the width of the control to be fully seen so that, given that this cannot happen if the text is wrapped, the scroll bar is unnecessary.

For more information about wrapping, see Labels That Wrap.

7.3.13 Clipboard Operations

Text controls provide the copy(), cut(), and paste() clipboard operations. Text controls also support platform-specific keystrokes to perform these operations within the control.

Some platforms provide a built-in Cut/Copy/Paste context menu. The clipboard operation methods are useful whenever you want to build your own context menu that contains these operations.

cut() Cuts the selection to the clipboard. The characters within the selection range are placed on the clipboard, then deleted from the text control.

copy() Copies the selection to the clipboard. The characters within selection range are placed on the clipboard. The text control is unaffected.

paste() Pastes text from the clipboard. The characters within selection range are replaced with the contents of the clipboard.

7.3.14 Scrolling

Text controls, along with the other controls that scroll in SWT, use the concept of top index to determine where to scroll. The top index of a text control is the zero-relative index of the topmost visible line in the control. For single-line text controls, the top index is always zero. The top index is set using the setTopIndex() method.

setTopIndex(int index) Scrolls by positioning the line indicated by the index at the top of the control. Indexing starts at zero. If the index is out of range, the control scrolls to show the closest index that is within the text contents. Depending on the platform and the number of lines, it may not be possible for the particular line to be placed at the top of the control. This can happen when the platform does not allow the control to scroll so that white space follows that last line.

getTopIndex() Returns the index of the line that is at the top of the control. The top index can change for many reasons other than calling setTopIndex(). For example, as the user enters new lines of text, the control may scroll, changing the top index.

Sometimes you might need to bring the selection to the attention of the user. The showSelection() method is used to scroll the control so that the selection is visible. Note that calling showSelection() after setSelection() is unnecessary because setSelection() already scrolls to show the new selection. However, there are circumstances where the selection may have changed and scrolled out of view.

showSelection() Scrolls to show the selection. The selection is made visible somewhere inside the control. If the selection is already visible, nothing happens.

7.3.15 Text Limits

Some programs, particularly those that interact with databases, might need to restrict the amount of text that can be entered into a text control. For example, they do this to ensure that they do not attempt to store more characters in a database field than it will accept.

setTextLimit(int limit) Sets the text limit. This is the number of characters that the control can contain. If the user attempts to type in more characters than the control can hold, the new characters are discarded. When this happens, the control issues a platform-specific warning, usually in the form of a short beep.You can limit the number of characters that a text control can contain using setTextLimit().

getTextLimit() Returns the text limit. If no limit has been set, this is the maximum number of characters that the control can contain.

7.3.16 Text Events

SWT.DefaultSelection (SelectionEvent)

The SWT.DefaultSelection event (typed event SelectionEvent) is sent whenever the user presses the <Enter> key for single-line text controls or uses a platform-specific mechanism to cause the event. Multiline text controls do not send the event, instead using the <Enter> key to insert a new line. The default selection event for a text control contains meaningful values in only the display, widget, and type fields.

SWT.Modify (ModifyEvent)

Table 7.1 shows the modify event for text controls.

Table 7.1. Modify Event

Untyped Event

Description

SWT.Modify

Text has changed in the control

Typed Event

Listener

Methods

ModifyEvent

ModifyListener

modifyText(ModifyEvent)


The SWT.Modify event (typed event ModifyEvent) is sent after characters have been inserted or deleted from a text control. The event is sent both when the user types and when the program changes the characters in the control. Modify events contain meaningful values in only the display, widget, and type fields.

Using SWT.Modify to Check Changes before Closing a Shell

The following example uses the SWT.Modify event to track the modified state of a text control. In the example, the text control is considered modified when characters have been entered that have not been saved. When the shell is closed, the user is prompted to save the changes, as shown in Figure 7.5. Note that when the program modifies the text control by setting the initial string, the modified flag must be cleared. This is necessary because SWT.Modify is sent any time the characters within the text control are changed.






public static void main(String[] args) {

    Display display = new Display();

    final Shell shell = new Shell(display);

    shell.setText("ModifyExample");

    shell.setLayout(new FillLayout());

    int style = SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER;

    Text text = new Text(shell, style);

    final boolean[] modified = new boolean [1];

    text.addListener(SWT.Modify, new Listener() {

        public void handleEvent(Event e) {

            modified[0] = true;

        }

    });

    shell.addListener(SWT.Close, new Listener() {

        public void handleEvent(Event e) {

            if (!modified[0]) return;

            int style = SWT.PRIMARY_MODAL |

            SWT.YES | SWT.NO | SWT.CANCEL;

            MessageBox box = new MessageBox(shell, style);

            box.setText(shell.getText());

            box.setMessage("Save changes?");

            switch (box.open()) {

                case SWT.YES:

                    System.out.println ("Saving ...");

                    // if (!saveText()) break;

                    //FALL THROUGH

                case SWT.NO :

                    break;

                case SWT.CANCEL :

                    e.doit = false;

                    break;

            }

        }

    });

    text.setText("Initial text.");

    modified[0] = false;

    shell.setSize(200, 200);

    shell.open();

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    display.dispose();

}


Figure 7.5.

graphics/07fig05.gif


This example is simple. It does not include the code that loads or saves the contents of the text control to a file. As well as performing the appropriate operation, code that loaded or saved the contents of the text control would need to clear the modified flag. This is necessary to allow the flag to be reset the next time the user types a character.

Using SWT.Modify to Warn the User of Invalid Input

Sometimes a program will warn the user with an error message when an invalid character is typed. This allows users temporarily to enter invalid input and fix it at their leisure. The SWT.Modify event is perfect for this task, because it is sent after the characters have been entered.

The following example warns the user when a character that is not a letter or a digit is typed into a text control. The warning message is displayed in a label that appears underneath the text control. To ensure that the warning message is valid, it is updated after every character. This ensures that when the user fixes the error, the warning message disappears.






public static void main(String[] args) {

    Display display = new Display();

    final Shell shell = new Shell(display);

    shell.setLayout(new RowLayout(SWT.VERTICAL));

    final Text text = new Text(shell, SWT.BORDER);

    text.setLayoutData(new RowData(250, SWT.DEFAULT));

    final Label label = new Label(shell, SWT.NONE);

    text.addListener(SWT.Modify, new Listener() {

        public void handleEvent(Event event) {

            String string = text.getText();

            int index = 0;

            while (index < string.length()) {

                char ch = string.charAt(index);

                if (!Character.isLetterOrDigit(ch)) break;

                index++;

            }

            if (index != string.length()) {

                label.setText(

                   "Must contain only letters and digits");

            } else {

                label.setText("");

            }

            shell.layout();

        }

    });

    shell.pack();

    shell.open();

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    display.dispose();

}


Figure 7.6 shows this example running after the user has typed the string "Hello?".

Figure 7.6.

graphics/07fig06.gif


SWT.Verify (VerifyEvent)

Table 7.2 shows the verify event for a text control.

Table 7.2. Verify Event

Untyped Event

Description

SWT.Verify

Text is to be validated in the control

Typed Event

Listener

Methods

VerifyEvent

VerifyListener

verifyText(VerifyEvent)


The SWT.Verify event (typed event VerifyEvent) is sent before characters are inserted or deleted from a text control. The event is sent both when the user types and when the program changes the characters in the control. The relevant event fields during a verify event for a text control are as follows.

Public Fields of Class Event That Are Valid during SWT.Verify

Field

Description

doit

Setting doit to false cancels the operation

text

The text that is about to be inserted

start

The start of the selection that will be replaced

end

The end of the selection that will be replaced


In contrast to the SWT.Modify event, because SWT.Verify is sent before the text control is changed, the program can filter the string that is about to be entered or cancel the operation. The following example uses the SWT.Verify event to allow only digits to be entered into a single-line text control:






public static void main(String[] args) {

    Display display = new Display();

    Shell shell = new Shell(display);

    Text text = new Text(shell, SWT.BORDER | SWT.SINGLE);

    text.setSize (text.computeSize (128, SWT.DEFAULT));

    text.addListener(SWT.Verify, new Listener() {

        public void handleEvent(Event e) {

            String text = e.text;

            char[] chars = new char[text.length()];

            text.getChars(0, chars.length, chars, 0);

            for (int i = 0; i < chars.length; i++) {

                if (!('0' <= chars[i] && chars[i] <= '9')){

                    e.doit = false;

                    return;

                }

            }

        }

    });

    shell.pack();

    shell.open();

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    display.dispose();

}


The SWT.Verify event contains sufficient information to maintain a copy of the contents of a text control. The following example implements a text control that emulates the SWT.PASSWORD style, inserting an asterisk for every character the user types or pastes into the text, as shown in Figure 7.7. The actual string that the user entered is stored in the variable string.






static String string = "";

public static void main(String[] args) {

    Display display = new Display();

    Shell shell = new Shell(display);

    Text text = new Text(shell, SWT.BORDER | SWT.SINGLE);

    text.setSize(text.computeSize(128, SWT.DEFAULT));

    text.addListener(SWT.Verify, new Listener() {

        public void handleEvent(Event e) {

            String prefix = string.substring(0, e.start);

            String suffix =

                string.substring(e.end, string.length());

            string = prefix + e.text + suffix;

            int length = e.text.length();

            e.text = "";

            for (int i = 0; i < length; i++) e.text += '*';

        }

    });

    shell.pack();

    shell.open();

    while (!shell.isDisposed()) {

        if (!display.readAndDispatch()) display.sleep();

    }

    System.out.println("String is " + string + ".");

    display.dispose();

}


Figure 7.7.

graphics/07fig07.gif


    Previous Section  < Day Day Up >  Next Section