Getting Your Feet Wet with the SWT StyledText Widget

来源:百度文库 编辑:神马文学网 时间:2024/04/29 06:07:26
Copyright© 2001-2004 International Business Machines Corp.
Eclipse Corner Article

Getting Your Feet Wet with the SWT
StyledText Widget
Summary
The StyledText widget is a customizable widget that can be used to display andedit text with different colors and font styles. This article presents an overview of theconcepts, issues, and rules that you should be aware of when using the StyledTextwidget.
By Lynne Kues and Knut Radloff, OTI
May 7, 2001; updated July 19, 2004 for Eclipse 3.0
Overview
The StyledText widget provides a fast and easy to use means to display and edittext. Within the StyledText widget, the following attributes can be specified:
text foreground color
text background color
text font style (i.e., normal, bold or italic)
line background color
These attributes are referred to as "styles". In addition, the StyledTextwidget provides standard navigation/editing keyboard behavior, allows for user-defined keybindings, and will perform word wrapping. Note that support for fonts with italic style wasadded as of version 3.0. However, the StyledText widget does not support mixing ofmultiple fonts.
The StyledText widget can be customized using pluggable objects to managetext content and styles. You can provide your own implementation of these objectsin order to tailor the widget to your needs. Or if you have no need to customizethe widget, easy to use API for managing styles and text content is available,making the pluggable nature of the widget completely transparent. How to customizethe StyledText widget and why you might want to do so will be discussedin our other article,Into the DeepEnd of the StyledText Widget.
The StyledText widget is used by the JFace Text Framework, which is part of the Eclipse Platform.The JFace Text Framework provides a layer of abstraction on top of the StyledText widget.The layer is pluggable and supports text formatting, content code assist capabilities, anda source viewer.
A Simple Example
The following is a simple example that shows how to create a StyledText widgetand set text. This example will be built upon in subsequent sections of this article.

import org.eclipse.swt.*;import org.eclipse.swt.custom.*;import org.eclipse.swt.graphics.*;import org.eclipse.swt.widgets.*;import org.eclipse.swt.layout.*;public class StyledTextExample {public static void main(String [] args) {// create the widget‘s shellShell shell = new Shell();shell.setLayout(new FillLayout());shell.setSize(200, 100);Display display = shell.getDisplay();// create the styled text widgetStyledText widget = new StyledText(shell, SWT.BORDER);widget.setText("This is the StyledText widget.");shell.open();while (!shell.isDisposed())if (!display.readAndDispatch()) display.sleep();}}Character, Line and Caret Offsets
Within the StyledText widget, both character offsets and line indexes are zerobased. For example, in the StyledText widget below, the line at index 0 is"abcde" and the character at offset 0 is "a".
widget.setText("abcde\r\nfghij");

When specifying offsets within the StyledText widget, line delimitercharacters are included.  In the above example, the line delimiter is CR/LF (i.e.,"\r\n").  Therefore, the character at offset five is CR and thecharacter at offset six is LF.  Similarly, getLineAtOffset(6)returns 0 and getLineAtOffset(7) returns 1. If there was another CR/LFline break at the end of the second line, getLineAtOffset(14) wouldreturn 2.
Caret offsets are also zero based.  Calling setCaretOffset(4) forthe above widget places the caret between ‘d‘ and ‘e‘.  And, like character offsets,caret offsets take line delimiters into account.  In order to place the caret at thebeginning of the second line (i.e., in front of the ‘f‘) you would call setCaretOffset(7).Also note that setCaretOffset(12) is a valid call and places the caret atthe end of the text, after the ‘j‘.  The range of valid caret offset values is onemore than the range of valid character offset values.
Text Ranges
Generally, the StyledText widget uses the start offset and the length to specifya text range. Specifying the length of a text range, instead of the end offset, is lesserror prone because there is no doubt as to whether or not the end offset is included inthe range. For example, a start offset of "0" and an end offset of "2"may or may not include the character at offset 2 (in SWT it doesn’t). Expressing theequivalent using a start offset and a length (i.e., 0 and 2 respectively) is unambiguous.It is clear that this describes the characters at offsets 0 and 1.
To maintain consistency with other SWT widgets, API methods that use the start offsetand end offset semantics are also provided. For example, SWT uses "start" and"end" when selecting items in a List, Tree or Table. Thetable below shows the API that uses start offset and end offset (non-range based) and theequivalent API with start offset and length semantics (range based).
Start Offset, End Offset Start Offset, Length
getSelection() getSelectionRange()
getText(int, int) getTextRange(int, int)
n/a redrawRange(int, int)
n/a replaceTextRange(int, int, String)
setSelection(int, int) setSelectionRange(int, int)
StyledText API that uses a start offset and a length will alwaysend with "Range". For example, getSelectionRange will return theselection as a start offset and a length.
You can mix non-range based and range based StyledText API, asdemonstrated by the code below.  The behavior is the same. However, for the sake ofclarity you should decide which kind of API to use and then use it consistently.
widget.setText("This is the StyledText widget.");widget.getText(5, 7); // returns "is"widget.getTextRange(5, 2); // returns "is"widget.setSelection(8, 11); // selects "the"Point sel = widget.getSelectionRange(); // returns Point with x=8, y=3widget.setSelectionRange(8, 3); // selects "the"Point sel = widget.getSelection(); // returns Point with x=8, y=11 Style Specification
The StyledText widget uses StyleRanges to specify text styles. The fieldsof the StyleRange class are defined as follows:
public int start; // start offset of the style, zero-based public int length; // length of the style public Color foreground; // text foreground color public Color background; // test background color public int fontStyle = SWT.NORMAL; // may be SWT.NORMAL, SWT.BOLD or SWT.ITALIC
If the foreground and background fields for a StyleRange are null,the StyledText widget will use the default widget colors for these fields.
The StyleRange class also includes helper methods for managing and manipulatingstyle ranges. For example, the similarTo(StyleRange) method will return whether ornot two style ranges have the same style attributes and only differ in the start andlength values.
You define style ranges for the StyledText widget by using the followingmethods:
public void setStyleRange(StyleRange range)public void setStyleRanges(StyleRange[] ranges)public void replaceStyleRanges(int start, int length, StyleRange[] ranges)
The setStyleRange API applies the specified style to the text range specified in theStyleRange. If the new style overlaps existing styles, the text outside the new style rangewill keep its existing style information.
The setStyleRanges API simply replaces all styles in the widget with the new ones.
The replaceStyleRanges API is provided to improve performance.It replaces any styles in the given range with the specified new styles. Allexisting styles in the range will be deleted, so the new styles need not coverthe entire replaced range. Note that replaceStyleRanges could be implementedby calling setStyleRange multiple times to cover the entire replace range. StyleRangeswith null colors would delete existing styles that have no replacement. However,the new API makes this task a lot easier and more efficient.
When text changes occur in the StyledText widget, styles that are set using any of the aboveAPI are updated to reflect the text changes. Styles are always maintained relative to the text to which theyare assigned and not to a fixed position. If text is inserted in front of a style, the style ranges after the insertedtext are updated to move with their associated text. If text is inserted at a location that is within a style range, thestyle range is split to accommodate the new text. If new text replaces existing text and some or all of the existingtext has a style assigned to it, only the non-replaced text will maintain its styles. In short, new text will neverbe automatically styled.
The following is a simple example of how to use style ranges in the StyledText widget.

widget.setText("This is the StyledText widget."); StyleRange styleRange = new StyleRange();styleRange.start = 0;styleRange.length = text.length();styleRange.fontStyle = SWT.BOLD;styleRange.foreground = display.getSystemColor(SWT.COLOR_BLUE);widget.setStyleRange(styleRange);
In addition to styles, the setLineBackground (int,int,Color) API can be used to customize the display of the text in the StyledTextwidget. The color specified when using this API will be applied to thedisplay area of the given lines (e.g., line background colors extendthewidth of the StyledText widget). If line background colors and styleswith background colors are used in conjunction with each other, thestyle background colors will be displayed after the line backgroundcolors are displayed (i.e., the style background color will takeprecedence forits given text range).
When text changes occur in the StyledText widget, line background colors that are specified via the setLineBackgroundAPI are maintained relative to the text lines to which they areassigned and not to a fixed position. Line background colors areupdated in a fashion similar to style ranges. New lines of text willnever be assigned line background colors and if an existing text linehas an associatedline background color, the association will be maintained.
Resource Life Cycle
SWT Color objects are used to specify styles for the StyledText widget.It is your responsibility to manage the life cycle of these objects. The colors must bedisposed when they are no longer used by the widget.
The StyledText widget references colors that are set using the replaceStyleRanges,setLineBackground, setStyleRange and setStyleRangesAPI methods.  Since the widget does notcopy Color objects, a color set using these API methods can only be consideredunused when all the character ranges and line ranges that use the color have been reset touse the default widget colors. The same is true for the setBackground and setForeground API.Colors set using this API are referenced by StyledText and can only be disposed when no longer set in thewidget or when the widget has been disposed.
The following code example illustrates the correct way to manage Colorobjects.  Two Colors, lime and orange, are created and used by the widget..

widget.setText("This is the StyledText widget."); // create the Colors Color orange = new Color(display, 255, 127, 0); Color lime = new Color(display, 127, 255, 127); // make "This" bold and orange StyleRange styleRange = new StyleRange(); styleRange.start = 0styleRange.length = 4; styleRange.fontStyle = SWT.BOLD; styleRange.foreground = orange;widget.setStyleRange(styleRange); // make "StyledText" bold and lime styleRange = new StyleRange();styleRange.start = 12; styleRange.length = 10;styleRange.fontStyle = SWT.BOLD; styleRange.foreground = lime; widget.setStyleRange(styleRange);// styleRange = new StyleRange(12, 10, null, null, SWT.NORMAL);// widget.setStyleRange(styleRange); // set the bold, lime colored text back to normal// lime.dispose(); // lime is no longer used by the widget so it can be disposedshell.open();while (!shell.isDisposed())if (!display.readAndDispatch()) display.sleep();orange.dispose();lime.dispose(); // make sure you comment out this line if you dispose lime above
If you decide that the lime colored text looks ugly, you can usethe code that is commented out to reset that text to the widget‘s default foregroundcolor. The example would then look like this:

Since you reset the only style range that uses the lime color,the lime Color object can be disposed. The orange color can only be disposed whenthe application terminates because it is still used by the first style range that was set.Note that we use a different StyleRange constructor for resetting the lime style tosimplify the style range creation.
The following code resets all style ranges to the widget‘sdefault colors.
widget.setStyleRange(null);orange.dispose();lime.dispose();
Once you have called setStyleRange(null) all colors can bedisposed of safely.
The setLineBackground API works exactly like the setStyleRangeAPI. You can only dispose Color objects after you have reset the line backgroundsthat use the colors or after you dispose the StyledText widget. For example:

widget.setText("This is the StyledText widget."); Color orange = new Color(display, 255, 127, 0);widget.setLineBackground(0, 1, orange);
You can dispose the orange color only after you have reset theline background color of the first line by calling:
widget.setLineBackground(0, 1, null);orange.dispose();
It is advisable for you to cache Color objects and dispose them whenyou dispose the StyledText widget. Caching colors will improve performanceand minimize the number of allocated resources. An in-depth discussion of SWTcolor resources can be found in theSWT Color Model article.
Text changes within the StyledText widget can occur via user input orprogrammatically. User input changes occur when keyboard input is received. Programmaticchanges occur when the StyledText API is executed. Four API methods exist forperforming programmatic changes:
public void append(String text)public void insert(String text)public void replaceTextRange(int start, int length, String text)public void setText(String text)
When text changes occur, the StyledText widget generates four kinds ofnotification events:
verify key event
verify event
modify event
extended modify event
The verify key event is sent immediately after a key has been pressed.  Thefields of the VerifyEvent that are used when sending the verify key eventare defined as follows:
public boolean doit; // flag indicating whether the keystroke should be processedpublic char character; // character represented by the key that was typedpublic int keyCode; // key code of the key that was typed. Used for special keys (e.g., CTRL). public int stateMask; // state of the keyboard modifier keys (e.g., SHIFT) at the time the // event was generated
You can use the verify key event to filter out a key stroke before itis processed by the StyledText widget. To do so, you would set the doit fieldof the VerifyEvent to false.
To listen to verify key events add a VerifyKeyListener to the StyledTextwidget and implement the verifyKey(VerifyEvent) method in the listener. Followingis an example of a VerifyKeyListener that prevents the user from deleting text byfiltering out the backspace and delete keystrokes.
// This handler will filter out the backspace and delete keys to prevent deleting characters. public void verifyKey(VerifyEvent event) {if ((event.character == ‘\u0008‘) || (event.character == ‘\u007F‘)) {event.doit = false;}}Verify Event
The verify event is sent when a text change isabout to occur because of user input. It is sent before the text content change is madeand before the change is reflected in the widget.  The fields of the VerifyEventthat are used when sending the verify event are defined as follows:
public int start, end; // range of text being modifiedpublic String text; // new text that will be inserted or empty stringpublic boolean doit; // flag indicating whether the text change should be processed
The VerifyEvent contains the start offset and end offset of the replaced text aswell as the new text. You should register a VerifyListener when you want to makechanges to the replace range or to the new text before the widget is updated. You canforce the widget to ignore the text change altogether by setting the doit field ofthe VerifyEvent to false.
You can use the verify event to perform operations such as automatic wordcompletion. Following is an example of a VerifyListener that performs wordcompletion. When you listen for verify events, you must implement the verifyText(VerifyEvent)method.
// This handler will automatically expand the character "S" to "StyledText".public void verifyText(VerifyEvent event) { // Only expand when text is inserted. if (event.end - event.start == 0) {if (event.text.equals("S")) {event.text = "StyledText";}}}
Note that the verify event that is sent by the StyledText widget is thesame event that is sent by the SWT Text and Combo widgets.
Modify Event
The modify event is sent after the widget‘s textcontent has been updated. It does not contain any information about the text change. Itserves as a light weight notification that a text change of some sort has occurred. Use itif all you want to know is if the widget text has been modified. For example, to determinewhether or not to display a "Save Changes?" prompter when an edited file isclosed. To listen to modify events add a ModifyListener to the StyledTextwidget.
The modify event that is sent by the StyledText widget is the same eventthat is sent by the SWT Text and Combo widgets.
Extended Modify Event
The extended modify event, which is sent after the modify event, contains thestart offset and length of the new text as well as the replaced text. It is the mirrorimage of the verify event.  The extended modify event is sent after thewidget has changed and when the text model reflects the actual text change. It, therefore,does not have a doit field like the verify key event and verify eventdo.
Register an ExtendedModifyListener if you need to update data after a textchange occurred and when you need to know the actual text change. For example, if you aredeveloping an editor for word processing that supports text styles, when a user types acharacter you will probably want that character to take on the styles of the charactersaround it. In order to implement this behavior, you will need to know where the textchange occurred, so that you can:
query the styles of the characters around the new character
change the style of the new character
The extended modify event will give you this information. Note that you cannotuse the verify event to implement the above scenario since the text content changehas not occurred yet when the verify event event is sent, so changing the style ofthe new character will be impossible. Similarly, you cannot use the modify event toimplement the above scenario because it does not contain information about the text change(e.g., where the text change occurred).
Note that a new event was introduced instead of enhancing the existing modify event inorder to maintain compatibility with SWT. Not all SWT widgets that send a modify eventcan get the data that is necessary to send an extended modify event.
Flow of Text Change Notifications
The following diagram depicts when the events described above occur within the StyledTextwidget. The "Process Input?" and "Process Change?" decisions aredetermined by the value of the boolean VerifyEvent.doit field as described above.The key binding processing shown in gray in the diagram is described in more detailbelow.
As shown in the diagram by the "Key Bound?" decision, if a key action isdefined for user input, text change processing will stop and the key action will beprocessed instead. In the verify key event example above, the backspaceand delete keys are editing actions.  So you must use a VerifyKeyListenervs. a VerifyListener to filter out these keystrokes.  VerifyListenersare only notified about content changes, not editing actions.
The Text Content/Styles Update process is also shown in gray.  This portion of thediagram will be discussed in depth in our second article, Into the Deep End of theStyledText Widget. For this article, knowing that the widget applies the textchange and updates its styles during this step is sufficient.

Text Notification Examples for User Input
The following table shows the event data that is sent when you type a character, whenyou delete a character and when you replace two characters with another character.  There is one column for each event object that is sent. The column header shows theevent object class and the Step number shown in the flow chart above.
When text changes occur, the VerifyEvent will contain the location of thereplaced text (as a start and end offset) and the new text, while the ExtendedModifyEventwill contain the location of the new text (as start and length) and the replaced text. TheModifyEvent is not shown since it does not have any data.
Note that when the verify key event is sent in Step 1, the start,end and text fields of its VerifyEvent are not used and are,therefore, not shown in the table. Likewise the character, keyCode and stateMaskfields are not used in the VerifyEvent that is created for Step 3.
The second scenario demonstrates that the VerifyKeyListener receives rawkeyboard input in the VerifyEvent. The VerifyEvent.character field is set to\u0008, which is the Unicode value of the backspace key.
Scenario
VerifyEvent Data (1)
sent to VerifyKeyListener
VerifyEvent Data (3)
sent to VerifyListener
ExtendedModifyEvent Data (6)
User types
character ‘A‘  doit = true
character = ‘A‘
keyCode = 0
stateMask = 0 start = 0
end = 0
text = "A"
doit = true start = 0
length = 1
replacedText = ""
User deletes
character ‘A‘  doit = true
character = \u0008 (backspace)
keyCode = 0
stateMask = 0 start = 0
end = 1
text = ""
doit = true start = 0
length = 0
replacedText = "A"
User replaces
"AA"
with ‘B‘
doit = true
character = ‘B‘
keyCode = 0
stateMask = 0 start = 0
end = 2
text = "B"
doit = true start = 0
length = 1
replacedText = "AA"
Text Notification Examples for API Text Changes
The following table illustrates the data for the change notification events undervarious API text change scenarios. The Initial Text column shows the text before thechange is made. The initial text of one scenario is the changed text of the previousscenario. There is one column for each event object that is sent. The column header forthese columns shows the event object class and the Step number shown in the flow chartabove.   A ^ character shows the insert point for new text, if any. The text replacerange, if any, is highlighted using white text on a blue background.
When text changes occur, the VerifyEvent will contain the location of thereplaced text (as a start and end offset) and the new text, while the ExtendedModifyEventwill contain the location of the new text (as start and length) and the replaced text. TheModifyEvent is not shown since it does not have any data.
Scenario
Initial Text
VerifyEvent Data (3)
ExtendedModifyEvent Data (6)
Insert "sopping "
before "wet"  Getting your feet wet ^ start = 18
end = 18
text = "sopping " start = 18
length = 8
replacedText = ""
Delete "y" Getting your feet sopping wet
start = 8
end = 9
text = "" start = 8
length = 0
replacedText = "y"
Replace "our"
with "my" Getting our feet sopping wet ^ start = 8
end = 11
text = "my" start = 8
length = 2
replacedText = "our"
Note that, as discussed above, you could change the replace range and the new text thatare passed in the VerifyEvent. For example, in the first scenarioyou could change the end offset of the replace range to 21 in order to replace the word"wet" and change the new text to "damp". The result would be"Getting your feet damp".
// This handler will change feet that are about to get "sopping" wet to "damp" feet. public void verifyText(VerifyEvent event) {if ((event.start == 18) && (event.end == 18) && (event.text.equals("sopping"))) {event.end = 21;event.text = "damp";}}
You could also set the VerifyEvent.doit field to false if you don‘t want to getsopping feet at all.
// This handler will prevent attempts to insert the word "sopping".public void verifyText(VerifyEvent event) {if (event.text.equals("sopping")) {event.doit = false;}}Text Refresh
Whenever the same text is redrawn at the same location in the StyledText widget,you will see flashing. Flash occurs because the StyledText widget first clears thearea to be drawn and then draws the text and its styles. Since nothing has changed, theredraw operation is perceived as a flash.
In order to avoid flash, you should minimize the amount of redrawing that occurs in thewidget. Unnecessary redrawing can occur when you use the setStyleRange API if thestyle you specify overlaps existing styles with the same colors and font style. The StyledTextwidget does not check for duplicate styles, so the entire style range will be redrawn andunchanged text will be refreshed. For the same reason you should also avoid replacing existing styleswith the same styles when using the replaceStyleRanges API.
For efficiency reasons, the StyledText widget does not perform duplicate styleoptimizations.  Users of the StyledText widget will most likely set stylesin the ExtendedModifyListener.  Since this listener is time-sensitive (i.e.,it is called every time a keystroke is entered), the StyledText widget avoidssuperfluous processing when styles are specified. Duplicate style optimizations could alsomask application problems.  If an application is creating duplicate styles, thiscould be a symptom of a larger problem with the application’s logic.
Similar refresh rules apply to the setLineBackground API. If you specify linebackground colors that already exist, you will see flash when unnecessary redrawingoccurs. Besides reducing flash, avoiding unnecessary redrawing will also improveperformance since less text will be rendered.
Key Bindings
You can change and query the default key bindings using the setKeyBinding(intkey, int action) and getKeyBinding(int key) methods. The setKeyBindingmethod takes a key code or character ORed with one or more of the modifiers SWT.CTRL,SWT.SHIFT or SWT.ALT as the first argument. SWT.java defines constants forsome of the frequently used key codes (e.g., SWT.BS for the backspace key). Thesecond argument is one of the actions defined in ST.java (e.g., ST.DELETE_PREVIOUS). Thus, to map + to work like the backspacekey and delete the character in front of the caret you would use
widget.setKeyBinding(SWT.CTRL | ‘b’, ST.DELETE_PREVIOUS)
You can map more than one  key to a single action, but one key can never triggermore than one action. If you use an already bound key combination to trigger a differentaction, the existing binding will be removed.
To remove a key binding you specify SWT.NULL as the action. For example, if youwant to remove the default key binding for +, which by defaultmoves the caret in front of the first character of the current page, you would use
widget.setKeyBinding(SWT.CTRL | SWT.PAGE_UP, SWT.NULL)
To find out which action is triggered by default when + ispressed you would use
widget.getKeyBinding(SWT.CTRL | SWT.HOME)
which returns ST.TEXT_START (sets the caret in front of the first character of thetext).
In addition to the setKeyBinding API, you can use the invokeAction methodwith one of the actions defined in ST.java to invoke an action independent ofkeyboard input or to handle multi-key actions. For example, to implement Emacs stylemulti-key actions you could use a VerifyKeyListener to listen for a key sequenceand call invokeAction to perform the desired action. Multi-key actions are actionsthat require two separate keystrokes, where the first keystroke establishes the contextfor the second keystroke. The following code snippet demonstrates how to implement amulti-key action. We assume that there is a boolean class variable calledpreviousCtrlX that we use to store whether the most recent keystroke was a+.
// This VerifyKeyListener implements multi-keystrokes using + as the // first keystroke and

as the second keystroke. These keystroke are ignore // by the StyledText widget since we set the VerifyEvent.doit field to false.widget.addVerifyKeyListener(new VerifyKeyListener() { public void verifyKey(VerifyEvent event) { // check whether the current keystroke is a +boolean isCtrlX = (event.stateMask == SWT.CTRL) && (event.character == ‘\u0018‘); // select one page if the previous keystroke was + and// the current keystroke is ‘P‘if (previousCtrlX && Character.toUpperCase(event.character) == ‘P‘) { widget.invokeAction(ST.SELECT_PAGE_DOWN);// ignore the second key of a multi-keystroke event.doit = false; } else if (isCtrlX) {// ignore + key strokes event.doit = false; }previousCtrlX = isCtrlX; }});
When pressing + followed by hitting

the exampleapplication looks like this

Any other + multi-keystroke will result in the secondkey being inserted in the widget since we only filter out the

key combination.
Conclusion
In this article we‘ve covered how to do the following within the StyledTextwidget:
create the widget
define and set text styles
handle text changes
manage Color objects
define key bindings
These functions cover basic text editing and rendering behavior.  However, if yourapplication has specialized editing or rendering needs, the StyledText widget canbe customized.  In our next article, we will go into depth about why you may want tocustomize the StyledText widget and how you would do this.