| <html> |
| |
| <head> |
| <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> |
| <title>Into the Deep End of the SWT StyledText Widget</title> |
| <link rel="stylesheet" href="../../default_style.css"> |
| </head> |
| |
| <body LINK="#0000ff" VLINK="#800080"> |
| <div align="right"> <font face="Times New Roman, Times, serif" size="2">Copyright |
| © 2001,2002 International Business Machines Corp.</font> |
| <table border=0 cellspacing=0 cellpadding=2 width="100%"> |
| <tr> |
| <td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font face="Arial,Helvetica"><font color="#FFFFFF"> Eclipse |
| Corner Article</font></font></b></td> |
| </tr> |
| </table> |
| </div> |
| <div align="left"> |
| <h1><img src="Idea.jpg" height=86 width=120 align=CENTER></h1> |
| </div> |
| <p> </p> |
| |
| <h1 ALIGN="CENTER">Into the Deep End of the</h1> |
| |
| <h1 ALIGN="CENTER">SWT StyledText Widget</h1> |
| |
| |
| <blockquote> |
| <b>Summary</b> |
| |
| <p>The<i> StyledText</i> widget is a customizable widget that can be used to display and |
| edit text with different colors and font styles. In this article we discuss why you might |
| want to customize the <i>StyledText</i> widget and how you would do that. The article |
| assumes you are familiar with the concepts presented in our other article, <a |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">"Getting |
| Your Feet Wet With the SWT StyledText Widget"</a>.</p> |
| <p><b>By Lynne Kues and Knut Radloff, OTI</b> |
| <br> |
| October 17, 2001; updated September 18, 2002 for Eclipse 2.0</p> |
| </blockquote> |
| |
| <hr width="100%"> |
| <h2>Overview</h2> |
| |
| <p>The <i>StyledText</i> widget is designed as a pluggable widget. You can supply any or |
| all of the following components in order to customize the widget to your needs: |
| |
| <ul> |
| <li> |
| <i>StyledTextContent</i> for storing and manipulating the widget’s text. The <i>StyledText</i> |
| widget interacts with its <i>StyledTextContent</i> in order to access and update the text |
| that is being displayed and edited in the widget. </li> |
| <li> |
| <i>LineStyleListener</i> for answering the style information that is associated with the |
| widget’s text. When the <i>StyledText</i> widget displays a line of text, it will ask |
| its <i>LineStyleListener</i> for the line’s style information. </li> |
| <li> |
| <i>LineBackgroundListener</i> for answering background colors for the lines that are |
| displayed by the widget. When the <i>StyledText</i> widget displays a line, it will ask |
| its <i>LineBackgroundListener</i> for the line’s background color. </li> |
| </ul> |
| |
| <p>The <i>StyledText</i> widget supplies default implementations for all of the above |
| objects. Therefore, you can supply any combination of the above components. For example, |
| you could supply just a <i>StyledTextContent</i> implementation and use the <i>StyledText</i> |
| widget’s default <i>LineStyleListener</i> and <i>LineBackgroundListener</i>.</p> |
| |
| <h2>Implementing a StyledTextContent</h2> |
| |
| <h3>StyledTextContent Applicability</h3> |
| |
| <p>One of the main reasons to provide a content implementation for the <i>StyledText</i> |
| widget is to avoid data duplication. If the text that is to be displayed by the <i>StyledText</i> |
| widget is already stored and maintained in an object, you can eliminate data duplication |
| by providing a content implementation. </p> |
| |
| <p>Providing your own <i>StyledTextContent</i> implementation will also give you more |
| flexibility since you will have direct access to the text content. For example, you may |
| want to manipulate the text independently of the widget. </p> |
| |
| <h3>StyledTextContent Method Overview</h3> |
| |
| <p>The <i>StyledTextContent </i>interface defines the protocol that is used by the <i>StyledText</i> |
| widget to get information about the text content it should display. The interface contains |
| all the methods that must be implemented in order to supply your own content |
| implementation. </p> |
| <i> |
| |
| <p>void addTextChangeListener(TextChangeListener listener)</i> |
| |
| <dir> |
| <p>This method is called by the <i>StyledText </i>widget in order to register itself as a |
| listener for content changes. The <i>StyledTextContent</i> implementer must store <i>TextChangeListeners |
| </i>and notify these listeners when content changes occur. Notification occurs in two |
| steps. First a <i>TextChangingEvent</i> should be sent when text content is about to |
| change and then a <i>TextChangedEvent</i> should be sent when the text content has |
| changed. Notification should occur in the <b>replaceTextRange</b> method (discussed |
| below).</p> |
| </dir> |
| |
| <address> |
| int getCharCount() |
| </address> |
| |
| <dl> |
| <dd>This method should return the number of characters in the text content. </dd> |
| </dl> |
| <i> |
| |
| <p>String getLine(int lineIndex)</i> |
| |
| <dir> |
| <p>This method should return the line at the given <i>lineIndex</i> without the line |
| delimiter. Line indexes are zero-based. For example:</p> |
| <pre><tt> line delimiter = "\r\n" |
| text content = "Line 1\r\nLine2\r\nLine 3" |
| </tt><i>getLine(0)</i><tt> should answer "Line 1" |
| </tt><i>getLine(2)</i><tt> should answer "Line 3"</tt></pre> |
| </dir> |
| <i> |
| |
| <p>int getLineAtOffset(int offset)</i> |
| |
| <dir> |
| <p>This method should return the index of the line at the given character <i>offset</i>. |
| Character offsets are zero-based. For example:</p> |
| <pre><tt> line delimiter = "\n" |
| text content = "Line 1\nLine2\nLine 3" |
| </tt><i>getLineAtOffset(6)</i><tt> should answer 0 |
| </tt><i>getLineAtOffset(7)</i><tt> should answer 1</tt></pre> |
| </dir> |
| <i> |
| |
| <p>int getLineCount()</i> |
| |
| <dir> |
| <p>This method should return the number of lines in the text content. |
| For example:</p> |
| <pre><tt> line delimiter = "\n" |
| text content = "Line 1\nLine2\nLine 3" |
| </tt><i>getLineCount()</i><tt> should answer 3</tt></pre> |
| </dir> |
| <i> |
| |
| <p>String getLineDelimiter()</i> |
| |
| <dir> |
| <p>This method should return the line delimiter that should be used when |
| inserting new lines (i.e., via the keyboard) into the text content.</p> |
| </dir> |
| <i> |
| |
| <p>int getOffsetAtLine(int lineIndex)</i> |
| |
| <dir> |
| <p>This method should return the character offset of the first character |
| for the given <i>lineIndex</i>. Character offsets are zero-based. For example:</p> |
| <pre><tt> line delimiter = "\r\n" |
| text content = "Line 1\r\nLine2\r\nLine 3" |
| </tt><i>getOffsetAtLine(1)</i><tt> should answer 8 |
| </tt><i>getOffsetAtLine(2)</i><tt> should answer 15</tt></pre> |
| </dir> |
| <i> |
| |
| <p>String getTextRange(int start, int length)</i> |
| |
| <dir> |
| <p>This method should return the text for the given range. The range begins |
| at character offset <i>start</i> and continues <i>length</i> characters. For example:</p> |
| <pre><tt> text content = "Line 1\r\nLine2\r\nLine 3" |
| </tt><i>getTextRange(3,8)</i><tt> should answer "e 1\r\nLin"</tt></pre> |
| </dir> |
| <i> |
| |
| <p>void removeTextChangeListener(TextChangeListener listener)</i> |
| |
| <ul> |
| <p>This method should remove a registered <i>TextChangeListener.</i> The |
| listener will no longer be notified of content changes.</p> |
| </ul> |
| <i> |
| |
| <p>void replaceTextRange(int start, int length, String newText)</i> |
| |
| <dir> |
| <p>This method should replace the given text range (beginning at offset <i>start</i> |
| and continuing for <i>replaceLength</i> characters) with <i>newText</i>. Before |
| the text update occurs, the <i>StyledTextContent</i> implementor should notify |
| its registered <i>TextChangeListeners</i> of the pending change. Notification |
| should occur via the <i>TextChangingEvent</i>.</p> |
| <dir> <tt> |
| <pre><font COLOR="#000080">public class</font> TextChangingEvent <font COLOR="#000080">extends</font> TypedEvent { |
| <font |
| COLOR="#000080"> public int</font> newCharCount; <font |
| COLOR="#800000">// the length of the new text |
| </font> <font |
| COLOR="#000080">public int</font> newLineCount; <font COLOR="#800000">// the number of new lines to be inserted |
| </font> <font |
| COLOR="#000080">public</font> String newText; <font COLOR="#800000">// the text to be inserted |
| </font> <font |
| COLOR="#000080">public int</font> replacedCharCount; <font COLOR="#800000">// the length of the text to be replaced |
| </font> <font |
| COLOR="#000080">public int</font> replacedLineCount; <font COLOR="#800000">// the number of lines to be replaced |
| </font> <font |
| COLOR="#000080">public int</font> start; <font COLOR="#800000">// the start offset of the text to be replaced |
| </font>}</pre> |
| </tt></dir> |
| <p>Note that <i>newLineCount<b> </b></i>and <i>replacedLineCount<b> |
| </b></i>should indicate the number of lines in the text to be inserted and the number of lines |
| in the text to be replaced, respectively. For example:</p> |
| </dir> |
| <div align="center"><center> |
| |
| <table BORDER="1" CELLSPACING="2" BORDERCOLOR="#000000" CELLPADDING="7" WIDTH="576"> |
| <tr> |
| <td WIDTH="22%" VALIGN="TOP"><p ALIGN="CENTER"><font FACE="Courier New" SIZE="2"><i>replaced |
| text</i></font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><i><p ALIGN="CENTER">inserted |
| text</i></font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><i><p ALIGN="CENTER">replacedLineCount</i></font></td> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><i><p ALIGN="CENTER">newLineCount</i></font></td> |
| </tr> |
| <tr> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">""</font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"\n"</font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">0</font></td> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">1</font></td> |
| </tr> |
| <tr> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"\n\n"</font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"a"</font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">2</font></td> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">0</font></td> |
| </tr> |
| <tr> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"a"</font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"\n\n"</font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">0</font></td> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">2</font></td> |
| </tr> |
| <tr> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">"\n"</font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">""</font></td> |
| <td WIDTH="28%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">1</font></td> |
| <td WIDTH="22%" VALIGN="TOP"><font FACE="Courier New" SIZE="2"><p ALIGN="CENTER">0</font></td> |
| </tr> |
| </table> |
| </center></div> |
| |
| <dir> |
| <p>After the text content is updated, the <i>StyledTextContent </i>implementor |
| should notify its registered <i>TextChangeListeners</i> that the change is complete. |
| Notification should occur via the <i>TextChangedEvent. </i>When the StyledText widget |
| receives this event, it will update the widget to reflect the change. The <i>TextChangedEvent</i> |
| does not contain any data since all the information about the change is included in the <i>TextChangingEvent</i>. |
| </p> |
| </dir> |
| |
| <p><em>void setText(String newText)</em><font FACE="Courier New" |
| SIZE="2"> </font> |
| |
| <dir> |
| <p>This method replaces the entire existing text with <i>newText</i>. Use |
| this method to initially specify the text content.</p> |
| </dir> |
| |
| <p>Note that the <i>StyledTextContent</i> implementation does not deal with |
| text styles. It is only responsible for managing the text content. |
| The <i>LineStyleListener</i> and <i>LineBackgroundListener </i>objects manage style information. When |
| you implement a <i>StyledTextContent</i>, you are not required to implement a <i>LineStyleListener</i> |
| or a <i>LineBackgroundListener</i>. <em>StyledText's</em> default implementations |
| of <em>LineStyleListener</em> and <em>LineBackgroundListener</em> will not change style |
| information when a content change occurs. Styles for existing text will remain as |
| before the content change and new text will not be assigned any styles. If a content |
| change requires style changes, you can use the <b>replaceStyleRanges</b> (new since Eclipse 2.0) and |
| <b>setStyleRange</b> API to update the style information. When to update style information is |
| discussed below in the "Text |
| Content/Style Update Processing" section</p> |
| |
| |
| <h3>StyledTextContent Rules</h3> |
| |
| <p>It is up to the <i>StyledTextContent</i> implementation to decide which |
| line delimiters it will recognize in order to determine the lines that are represented in |
| a text string. The default <i>StyledTextContent</i> implementation that is supplied with |
| the <i>StyledText</i> widget recognizes any combination of "\n", |
| "\r\n", and "\r" as line delimiters. Doing so ensures that strings |
| created with different line delimiters are interpreted correctly. Note that the delimiter |
| for a specific platform comes into play when new lines are inserted. When inserting text, |
| you want the new lines to be created with the platform line delimiter so that copy and |
| paste operations between platform applications work properly. You specify the delimiter to |
| use when inserting new text via the <b>getLineDelimiter </b>method. </p> |
| |
| |
| <p>As discussed above, when a text change occurs, the <i>StyledTextContent</i> |
| needs to send a <i>TextChangingEvent</i> and a <i>TextChangedEvent</i> to notify the <i>StyledText</i> |
| widget about the text change. Text changes can occur in one of three ways: |
| |
| <ol TYPE="A"> |
| <li>via the <i>StyledText</i> widget because of user input (e.g., keyboard |
| entry)</li> |
| <li>via the <i>StyledText</i> widget because API was called (e.g., <b>replaceTextRange |
| </b>or <b>paste</b>)</li> |
| <li>via your <i>StyledTextContent</i> implementation (i.e., your application |
| changes text independent of the <i>StyledText</i> widget)</li> |
| </ol> |
| |
| <p>The following diagram indicates how these text changes occur within the <em>StyledText</em> |
| widget. The boxes in gray are discussed in length in our <a |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a>, |
| "Getting Your Feet Wet With the SWT StyledText Widget". For this article, |
| we will expand upon the processing that occurs in Step 4, "Text Content/Style Update".</p> |
| |
| <p ALIGN="CENTER"><img src="TextChanges.gif" width="420" height="995" |
| alt="TextChanges.gif (12968 bytes)"></p> |
| <div align="center"><b>Figure 1 – Text Change Processing</b> </div> |
| |
| <h4>Text Content/Style Update Processing </h4> |
| |
| |
| <p>The Text Content/Style Update process begins with the <em>StyledText</em> |
| widget or your application (for Case C) calling the<strong> replaceTextRange(int, int, |
| String)</strong> method. This method is the time and place to send the text change |
| events and to update your text content. How processing should occur is |
| depicted in the diagram below. The light gray boxes represent the processing that your <i>StyledTextContent</i> |
| implementation should perform, while the dark gray boxes represent the processing that |
| occurs in the <i>StyledText</i> widget.</p> |
| <font FACE="Courier New" SIZE="2"> |
| |
| <p ALIGN="CENTER"> <img src="TextUpdate.gif" width="607" height="796" |
| alt="TextUpdate.gif (11618 bytes)"></p> |
| </font> |
| <div align="center"><b>Figure 2 – Text Content/Style Update Processing</b> </div> |
| <p>The text content/style update process is started by sending the <i>TextChangingEvent</i> |
| (1). In response to this event, the <i>StyledText</i> widget prepares for the text change |
| by posting screen update operations for the area of the text change (2). After the <i>TextChangingEvent</i> |
| is sent, the application should update its text content to reflect the change (3), apply |
| style changes (4), and send the <i>TextChanged</i> event (5). When the <i>StyledText</i> |
| widget receives the <i>TextChangedEvent</i> it will update itself in accordance with the |
| change (e.g., scroll bars will be adjusted) and the text change will be visibly presented |
| in the <i>StyledText</i> widget (6). If you don't supply a <i>LineStyleListener</i> |
| as described below, part of the text change process will include updating the |
| style ranges to accommodate the text change (occurs in Step 2). As discussed in the first article |
| <a HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html"> |
| "Getting Your Feet Wet With the SWT StyledText Widget"</a>, the StyledText widget maintains |
| styles for existing text and does not apply styles for any new text. If the new text requires |
| styles or the existing styles need to be modified, this should be done in Step 4. </p> |
| |
| <p>Note that the rules that apply to updating styles also apply to updating line background colors. If you do |
| not supply a <i>LineBackgroundListener</i> as described below, part of the text change process will |
| include updating line background colors if the text change includes adding or removing lines. As with styles, |
| the <i>StyledText</i> widget maintains existing line background colors. If the line |
| background colors need to be updated as a result of the text change, this should be done in Step 4. |
| </p> |
| |
| <p>Note that between the time that your <em>StyledTextContent</em> |
| implementation sends the <i>TextChangingEvent</i> and the <i>TextChangedEvent</i>, your |
| implementation should not use API that will cause a scroll operation to occur or that will |
| cause posted paints to be flushed. The <i>StyledText</i> widget also does not handle |
| nested text content updates, so when processing text content changes, the application |
| should not use API that will cause text updates to occur.</p> |
| |
| <p>The <i>StyledText</i> widget only modifies the cursor location when text |
| is changed via user input. In all other instances (i.e., Case B and Case C in Figure 1 |
| above), if the application wishes the cursor location to change as a result of the text |
| content change, the following methods can be used to set the cursor location to an offset |
| and to make that offset visible: </p> |
| <tt> |
| |
| <pre> setSelection(offset, 0) |
| showSelection()</pre> |
| </tt> |
| |
| <h3>StyledTextContent Considerations</h3> |
| |
| <p>When implementing a <i>StyledTextContent</i>, the algorithm for updating |
| the text content when modifications occur must be fast. If it is not, you will experience |
| sluggishness during typing (i.e., as your <i>StyledTextContent</i> attempts to deal with |
| the text change). Similarly, the algorithm for returning the text for a line should be |
| fast. If it is not, you will experience sluggishness during scrolling. It is also |
| important to ensure that your implementation scales well. When the text content gets |
| larger, handling of text replace operations and answering lines should not be adversely |
| affected from a performance standpoint. </p><p> |
| |
| For an example of a <i>StyledTextContent</i> implementation, refer to the class <i>org.eclipse.swt.custom.DefaultContent</i>. |
| <i>DefaultContent</i> is the content implementation that the <i>StyledText</i> widget uses |
| by default. </p> |
| |
| |
| <h3>StyledTextContent Implementation Validation</h3> |
| |
| <p>The following Java™ class can be used to test a <i>StyledTextContent</i> |
| implementation. When executed, the class will run through a number of test scenarios that |
| exercise the <i>StyledTextContent</i> interface. To use the class, import it into the |
| package that contains your <i>StyledTextContent</i> implementation. Then specify the fully |
| qualified name of your implementation class as an argument to the<i> |
| StyledTextContentSpec’s</i> <b>main</b> method. As each test scenario is |
| executed, <b>System.out</b> will be updated to indicate whether or not each test |
| passed. Note that this class is designed to work with a <em>StyledTextContent</em> |
| implementation that supports "\r", "\n", and '\r\n" as line |
| delimiters.</p> |
| |
| <p><a HREF="StyledTextContentSpec.java">StyledTextContentSpec.java</a> <br> |
| |
| </p> |
| |
| |
| <h2>Implementing a LineStyleListener</h2> |
| |
| <h3>LineStyleListener Applicability</h3> |
| |
| <p>One of the main reasons to use a customized <i>LineStyleListener</i> is |
| to support on-demand line styling. Instead of using the <b>replaceStyleRanges</b> (new in Eclipse 2.0), |
| <b>setStyleRanges</b> and <b>setStyleRange</b> |
| methods to statically set the style information for the widget’s text, |
| style information can be provided dynamically on a line-by-line basis. </p> |
| |
| <p>If styles are determined based on the actual text content, on-demand line styling can |
| be used since the text of each line is passed to the <i>LineStyleListener</i>. Text styles |
| that are used for syntax highlighting can typically be provided dynamically. For example, |
| except for block comments, all of Java’s syntactic elements occur on one line. It |
| would be more efficient to use a dynamic, customized<i> LineStyleListener </i>vs. the <i>StyledText</i> |
| API to implement Java syntax highlighting. If you used the <i>StyledText</i> API, style |
| information would be maintained statically and any time text changes occurred this |
| information would have to be updated (e.g., via the <b>setStyleRange</b> API). Using the <i>StyledText</i> |
| API would also require you to parse the entire text up front. If on-demand line styling |
| were used, the text would be parsed on an as needed basis, as it is displayed, thus |
| improving initial load time. </p> |
| |
| <p>Another reason to use a customized <i>LineStyleListener</i> is to avoid duplicating |
| data. When the <i>StyledText</i> methods <b>replaceStyleRanges</b>, <b>setStyleRanges</b> and <b>setStyleRange </b>are |
| called, the <i>StyledText</i> widget will cache the <i>StyleRange</i> objects that are |
| supplied as input parameters. However, if the information needed to construct style ranges |
| is already stored by an object in your application, introducing a <i>LineStyleListener</i> |
| that uses this object may be beneficial. </p> |
| |
| <p>You may also use a customized <i>LineStyleListener</i> in order to provide |
| application-specific text coloring behavior. By default, when text changes occur in the <i>StyledText</i> |
| widget, text colors are updated as follows: |
| |
| <ol> |
| <ol> |
| <li>If text is deleted, its associated style will also be deleted.</li> |
| <li>If new text is inserted, it’s associated style will be null (i.e., |
| the text background color will be the widget background color, the text foreground color |
| will be the widget foreground color, and the text font style will be the widget font |
| style).</li> |
| </ol> |
| </ol> |
| |
| <dir> |
| <dir> |
| <dir> |
| <p>Note: A text replace operation is treated as a text delete operation |
| followed by a text insert operation.</p> |
| </dir> |
| </dir> |
| </dir> |
| |
| <p>The <i>StyledText</i> widget does not make any assumptions about how to |
| apply styles when text is inserted or deleted because how to update the styles will depend |
| on how the <i>StyledText</i> widget is being used. For example, if the widget is being |
| used to support syntax highlighting for a source code editor, coloring will occur only |
| when inserted text completes a keyword. Whereas, if the widget is being used to support |
| text attributes for a word processor, inserted text will most likely take on the style |
| values of the characters adjacent to the text. To provide specific text style updating |
| behavior you can introduce your own <i>LineStyleListener</i>.</p> |
| |
| |
| <h3>LineStyleListener Method Overview</h3> |
| |
| <p>The <i>LineStyleListener </i>interface defines the protocol that is used |
| by the <i>StyledText</i> widget to get the styles of a displayed line.</p> |
| <i> |
| |
| <p>void lineGetStyle (LineStyleEvent event) </i> |
| |
| <dir> |
| <p>The <b>lineGetStyle</b> method is called when a line is about to be |
| drawn in order to get the line’s style information. When the method is called, the <i>LineStyleEvent</i> |
| has two input fields set - <b>LineStyleEvent.lineOffset</b> contains the start offset of |
| the line and <b>LineStyleEvent<font COLOR="#000080">.</font>lineText</b> contains |
| the text of the line. It is your responsibility to set the output field, <b>LineStyleEvent.styles</b>. |
| This field defines the StyleRanges for the text line. The widget uses the StyleRanges for |
| measuring and rendering text when the <b>lineGetStyle</b> listener method returns. Each <i>StyleRange</i> |
| object describes the foreground color, background color and font style for a range of text |
| in the particular line your listener was called for. The range start offset is relative to |
| the beginning of the document, not the beginning of the line.</p> |
| </dir> |
| |
| <h3>LineStyleListener Rules</h3> |
| |
| <p>If you implement your own <i>LineStyleListener </i>to supply line styles |
| you may no longer use the <b>replaceStyleRanges</b>, <b>setStyleRange, setStyleRanges</b> or <b>getStyleRange</b> |
| API. You can supply style information using a <i>LineStyleListener </i>or the API, but not |
| both. </p><p> |
| |
| Ideally, you only have to use the line text and offset information supplied in the <i>LineStyleEvent</i> |
| passed in to the <b>lineGetStyle</b> listener method to calculate line style information. |
| However, it may be necessary for you to store style information and simply look up the |
| styles when your listener method is called. In this case, you will have to update the |
| style information when text changes occur. To do this, you can listen to the <b>extended |
| modify event</b> (described in detail in our <a |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a>). |
| The <b>extended modify event</b> is sent after the widget text has been updated, but |
| before the screen updates take effect. In this manner, inserted text can immediately use |
| any styles that have been specified. Note that it does not make sense to use the <b>modify |
| event</b> to update styles in this manner since the <b>modify event</b> does not contain |
| any specific information about the text change (e.g., character offset of the inserted |
| text). <a NAME="StyleRedraw"></a> </p> |
| |
| <p>If, as a result of the text change, style information before or after the lines |
| modified by the text change is changed, you will have to redraw the affected area of the |
| widget. For example, if you are using the <i>StyledText</i> widget for java syntax |
| highlighting in a source code editor, deleting the beginning of a block comment (i.e., |
| "/*") will affect lines after the line on which the text change occurred. In |
| this instance, it would be the application’s responsibility to redraw the other |
| comment lines. The <i>StyledText</i> widget will only update text lines based on the text |
| change that occurred.</p> |
| |
| <p>The <i>StyledText</i> widget provides two redraw methods to support the above |
| scenarios: </p> |
| <tt> |
| |
| <pre> <font COLOR="#000080">public void</font> redrawRange(<font COLOR="#000080">int</font> start, <font |
| COLOR="#000080">int</font> length, <font COLOR="#000080">boolean</font> clearBackground) |
| <font |
| COLOR="#000080">public void</font> redraw(<font COLOR="#000080">int</font> x, <font |
| COLOR="#000080">int</font> y, <font COLOR="#000080">int</font> width, <font |
| COLOR="#000080">int</font> height, <font COLOR="#000080">boolean</font> all)</pre> |
| </tt> |
| |
| <p>The first, <b>redrawRange</b>, lets you specify the exact text range to |
| redraw using character offsets. The second method, <b>redraw</b>, is the standard SWT |
| redraw method that redraws a pixel based rectangle in the widget. SWT provides another |
| redraw method, which is not shown here. It does not have any arguments and redraws the |
| whole widget. You can use this method, but it will cause the text to flash since it |
| performs a full clear and redraw of the widget.</p> |
| |
| <h3>LineStyleListener Considerations</h3> |
| |
| <p>If you implement your own <i>LineStyleListener</i>, keep in mind that |
| the listener is called every time a line is rendered or scrolled and when a key is |
| pressed. It is therefore important that the <b>lineGetStyle</b> method is fast and that it |
| scales well for use with large amounts of text. If you experience a lag when typing and |
| scrolling, your <i>LineStyleListener </i>implementation is too slow. If typing and |
| scrolling is slow at the end of the text, your algorithm does not scale well. Note that |
| unresponsive typing may also be caused by slow processing in <i>VerifyListeners</i>, <i>ModifyListeners</i> |
| or <i>ExtendedModifyListeners </i>(see our <a |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a> |
| for more information about verify and modify listeners). </p> |
| |
| <p>If you implement a <i>LineStyleListener </i>and store the <i>StyleRange</i> objects |
| (instead of calculating them on demand) it is advisable to merge adjacent styles that have |
| the same style data. Merging StyleRanges improves the performance of text measuring and |
| rendering in the <i>StyledText</i> widget because fewer <i>StyleRange</i> objects have to |
| be processed. </p> |
| |
| <p><a NAME="LineStyleListenerRedraw"></a>If you redraw text during the <b>extended |
| modified event</b>, make sure that you only redraw text ranges that actually have new |
| styles. Unnecessary refresh operations can cause flash and degrade performance. See the |
| discussion in the "Text Refresh" section of our <a |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a> |
| for more information about what flash means and what causes it. </p> |
| |
| <p>For an example of a <i>LineStyleListener</i> implementation, refer to the class <i>org.eclipse.swt.custom.DefaultLineStyler</i>. |
| This class’ implementation of the <i>LineStyleListener</i> interface is the |
| implementation that the <i>StyledText</i> widget uses by default. The class illustrates |
| the concepts discussed above, including how to merge neighboring styles </p> |
| |
| <h3>LineStyleListener Example</h3> |
| |
| <p>The following example is the <b>lineGetStyle</b> method from a <i>LineStyleListener |
| </i>that implements a simple Java syntax coloring strategy. The method constructs |
| the <em>StyleRange</em> objects dynamically (i.e., every time they are requested) |
| and merges similar styles for improved rendering performance. The example |
| method code is taken from the SWT Java Syntax Viewer example, which is part |
| of the example plugins that can be downloaded with the Eclipse SDK. The |
| example code can be found in the plugins subdirectory<em> </em>org.eclipse.swt.examples. |
| Three classes comprise the example - <em>JavaViewer</em>, <em>JavaLineStyler</em> |
| and <em>JavaScanner</em>. The <em>JavaScanner</em> class is an inner |
| class of <em>JavaLineStyler</em>.</p> |
| |
| <p>The <i>JavaScanner</i> class implements a Java syntax scanner. The <i>JavaScanner</i> |
| public API includes the methods: </p> |
| <tt> |
| |
| <pre> <font COLOR="#000080">void</font> setRange(String) |
| <font COLOR="#000080">int</font> nextToken() |
| <font |
| COLOR="#000080">int</font> getStartOffset() |
| <font COLOR="#000080">int</font> getLength()</pre> |
| </tt> |
| |
| <p>as well as constants for the different Java syntax elements that need |
| highlighting. In addition, the method <b>Color getColor(int)</b> returns a <i>Color</i> |
| object for a given Java syntax constant. </p> |
| |
| <p>In the example, we set the text of the line we want to syntax scan in the Java scanner. |
| We apply different colors to each token and keyword tokens are given a font style of bold. |
| If the color for a token differs from the widget foreground color or if the token is a |
| keyword (in which case a <i>StyleRange</i> would be necessary in order to specify a bold |
| font style), we create a new <i>StyleRange</i> object for that element. If the previous <i>StyleRange</i> |
| object has the same foreground color, background color and font style, it is considered |
| similar and the two ranges can be merged. Merging StyleRanges improves the performance of |
| text measuring and rendering in the <i>StyledText</i> widget.</p> |
| |
| <p>The lineGetStyle method also includes special processing for handling the <i>StyleRanges</i> |
| that are created for keywords. In Java code, multiple keywords are often grouped together |
| (e.g., "public static final int"). To minimize the number of <i>StyleRanges</i> |
| that are created in this instance, spaces between the keywords are assigned a bold font |
| style. This strategy will lead to only one <i>StyleRange</i> for a group of keywords vs. |
| multiple <i>StyleRanges</i> (i.e., one for each keyword and one for each space between the |
| keywords). The strategy will also minimize the number of font style changes that |
| occur during rendering (i.e., the number of changes from the normal font to the bold |
| font).</p> |
| |
| <p>When the complete line has been scanned and processed, the styles are copied into the <i>LineStyleEvent</i>. |
| |
| |
| <dir> |
| <dir> |
| <tt><pre><font COLOR="#000080">public</font> <font COLOR="#000080">void</font> lineGetStyle(LineStyleEvent event) { |
| Vector styles = <font |
| COLOR="#000080">new</font> Vector(); |
| |
| <font COLOR="#800000"> // If the line is part of a block comment, create one style for the entire line. |
| </font> <font |
| COLOR="#000080">if</font> (inBlockComment(event.lineOffset, event.lineOffset + event.lineText.length())) { |
| styles.addElement(new StyleRange(event.lineOffset, event.lineText.length(), getColor(COMMENT), <font |
| COLOR="#000080">null</font>)); |
| event.styles = <font COLOR="#000080">new</font> StyleRange[styles.size()]; |
| styles.copyInto(event.styles); |
| <font |
| COLOR="#000080">return</font>; |
| } |
| |
| <font COLOR="#000080"> int</font> token; |
| StyleRange lastStyle; |
| Color defaultFgColor = ((Control)event.widget).getForeground(); |
| scanner.setRange(event.lineText); |
| token = scanner.nextToken(); |
| <font |
| COLOR="#000080">while</font> (token != EOF) { |
| <font COLOR="#000080">if</font> (token == OTHER) { |
| <font |
| COLOR="#800000"> // no syntax highlighting necessary |
| </font> } <font |
| COLOR="#000080">else</font> <font COLOR="#000080">if</font> ((token != WHITE) { |
| <font |
| COLOR="#800000"> // Only create a style if the token color is different than the widget's default foreground color and the |
| // token’s fontStyle is not bold. |
| </font> Color color = getColor(token); |
| <font |
| COLOR="#000080"> if</font> ((!color.equals(defaultFgColor)) || (token == KEY)) { |
| StyleRange style = new StyleRange(scanner.getStartOffset() + event.lineOffset, scanner.getLength(), color, <font |
| COLOR="#000080">null</font>); |
| <font COLOR="#000080">if</font> (token == KEY) { |
| style.fontStyle = SWT.BOLD; |
| } |
| <font |
| COLOR="#000080">if</font> (styles.isEmpty()) { |
| styles.addElement(style); |
| } <font |
| COLOR="#000080">else</font> { |
| <font COLOR="#800000"> // Merge similar styles. |
| </font> lastStyle = (StyleRange)styles.lastElement(); |
| <font |
| COLOR="#000080">if</font> (lastStyle.similarTo(style) && (lastStyle.start + lastStyle.length == style.start)) { |
| lastStyle.length += style.length; |
| } <font |
| COLOR="#000080">else</font> { |
| styles.addElement(style); |
| } |
| } |
| } |
| } <font |
| COLOR="#000080">else if</font> (!styles.isEmpty()) && ((lastStyle=(StyleRange)styles.lastElement()).fontStyle == SWT.BOLD)) { |
| <font |
| COLOR="#800000"> // Have the white space take on the bold style before it to minimize the number of style ranges. |
| </font> <font |
| COLOR="#000080">int</font> start = scanner.getStartOffset() + event.lineOffset; |
| <font |
| COLOR="#800000"> </font><font COLOR="#000080">if</font> (lastStyle.start + lastStyle.length == start) { |
| <font |
| COLOR="#800000"> </font>lastStyle.length += scanner.getLength(); |
| } |
| } |
| token= scanner.nextToken(); |
| } |
| event.styles = new StyleRange[styles.size()]; |
| styles.copyInto(event.styles); |
| } |
| </pre> |
| </tt></dir> |
| </dir> |
| |
| |
| <h2>Implementing a LineBackgroundListener</h2> |
| |
| <h3>LineBackgroundListener Applicability</h3> |
| |
| <p>As with the LineStyleListener, one reason to use a customized <i>LineBackgroundListener</i> |
| is to avoid duplicating data. When the <i>StyledText</i> method <b>setLineBackground</b> |
| is called, the <i>StyledText</i> widget will cache the colors for the specified lines. If |
| this information is already stored by an object in your application, introducing a <i>LineBackgroundListener</i> |
| that interacts with this object will eliminate data duplication. </p> |
| |
| <p>You may also use a customized <i>LineBackgroundListener</i> in order to provide |
| application-specific line background coloring behavior. The <i>StyledText</i> widget |
| maintains line background colors relative to text lines. By default, when text changes |
| occur in the <i>StyledText</i> widget, line background colors are updated as follows: |
| |
| <ol> |
| <ol> |
| <li>If a line is deleted, its associated line background color will also be deleted.</li> |
| <li>If a new line is inserted, its associated line background color will be null (i.e., the |
| widget background color) and line background colors for existing lines of text are |
| unchanged.</li> |
| </ol> |
| </ol> |
| |
| <p>For example, in the window below, a line background color of gray is used to emphasize |
| the import statements. If text lines are inserted or deleted, you would want the line |
| background color that is associated with the block of import statements to be maintained. </p> |
| |
| <p ALIGN="CENTER"><img SRC="Image8.gif" WIDTH="427" HEIGHT="169"></p> |
| <p> |
| |
| <font SIZE="2"><br> |
| </font>Your application may want line background colors to be updated |
| differently when text changes occur. For example, if you want the background of every |
| other line to be gray (e.g., in order to facilitate reading tabulated text), you would |
| need to "shift" line background colors as lines are deleted or inserted. You |
| could easily support this behavior by implementing your own <i>LineBackgroundListener</i>.</p> |
| |
| |
| <h3>LineBackgroundListener Method Overview</h3> |
| |
| <p>The <i>LineBackgroundListener</i> interface defines the protocol that is |
| used by the <i>StyledText</i> widget to get the line background colors to use when |
| displaying the text. The interface, <i>LineBackgroundListener</i>, contains the methods |
| that must be implemented in order to supply your own line background color implementation. |
| </p> |
| <i> |
| |
| <p>void lineGetBackground(LineBackgroundEvent event)</i> |
| |
| <dir> |
| <p>This method is called when a line is to be drawn in order to get the line’s |
| background color. <i>LineBackgroundEvent</i> will contain the start offset, <b>lineOffset</b>, |
| and the text, <b>lineText</b>, of the line to be drawn. Fill in the background color in |
| the <b>lineBackground</b> field. A value of null indicates that the default widget |
| background should be used for the line. <br> |
| </p> |
| </dir> |
| |
| <p>In the following example, a LineBackgroundListener is used to color the background of |
| every other line to light gray. The method assumes that we have a cache of colors indexed |
| by RGB value. |
| |
| <dir> |
| <dir> |
| <tt><pre><font COLOR="#000080">public void</font> lineGetBackground(LineBackgroundEvent event) { |
| <font |
| COLOR="#000080">int</font> line = ((StyledText) event.widget).getLineAtOffset(event.lineOffset); |
| <font |
| COLOR="#000080">if</font> ((line % 2) == 1) { |
| event.lineBackground = (Color)colors.get(<font |
| COLOR="#000080">new</font> RGB(222, 222, 222)); |
| } |
| }</pre> |
| </tt></dir> |
| </dir> |
| |
| |
| <h3>LineBackgroundListener Rules</h3> |
| |
| <p>Line background colors are drawn for the width of the widget, not the |
| width of the text. If a text background color is defined (i.e., via a <i>StyleRange</i>), |
| this color will overlay the line background color. </p> |
| |
| <p>If you implement your own <i>LineBackgroundListener, </i>the following <i>StyledText</i> |
| methods are not applicable and should not be used since your <i>LineBackgroundListener</i> |
| will be maintaining and storing line background colors: |
| |
| <dir> |
| <dir> |
| <p><tt><font COLOR="#000080">public</font> Color getLineBackground(<font COLOR="#000080">int</font> |
| index)</tt><font SIZE="2"> <br> |
| </font><tt><font COLOR="#000080">public</font> <font COLOR="#000080">void</font> |
| setLineBackground(<font COLOR="#000080">int</font> startLine, <font COLOR="#000080">int</font> |
| lineCount, Color background)</tt> </p> |
| </dir> |
| </dir> |
| |
| |
| <h3>LineBackgroundListener Considerations</h3> |
| |
| <p>When you implement a <i>LineBackgroundListener</i>, since the <b>setLineBackground</b> |
| API is no longer available, you will be responsible for refreshing line background colors |
| within the widget. For example, if a text content change alters line background colors for |
| lines other than the lines on which the text change occurred, it would be the |
| LineBackgroundListener’s responsibility to redraw these lines. You can refresh line |
| background colors by using the <b>redraw(int x, int y, int width, int</b><b>height, boolean all)</b> method. The input parameters |
| to this method specify a pixel-based rectangle. You can use other <i>StyledText</i> API |
| (e.g., <b>getLocationAtOffset</b>) to calculate the rectangle for a line. </p><p> |
| |
| <a NAME="LineBackgroundListenerRedraw"></a>When you implement a <i>LineBackgroundListener</i> |
| you should make sure that you only initiate line background updates when necessary. |
| Unnecessary refreshing of lines will cause flash and degrade performance of the <i>StyledText</i> |
| widget, as discussed in the Text Refresh section of our <a |
| HREF="http://www.eclipsecorner.org/articles/StyledText%201/article1.html">first article</a>.</p><p> |
| |
| For an example of how to implement a <i>LineBackgroundListener</i>, refer to the class <i>org.eclipse.swt.custom.DefaultLineStyler</i>. |
| The <i>DefaultLineStyler </i>implements the<i> LineBackgroundListener</i> behavior that |
| the <i>StyledText</i> widget uses by default. |
| |
| <dl> |
| <dt> </dt> |
| </dl> |
| |
| <p><small>Java and all Java-based trademarks and logos are trademarks or registered |
| trademarks of Sun Microsystems, Inc. in the United States, other countries, or |
| both.</small></p> |
| |
| </body> |
| </html> |