Bug 4610 - StyledText - DCR - Word wrap (1GABS6C)
Summary: StyledText - DCR - Word wrap (1GABS6C)
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 2.0   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 2.0 M2   Edit
Assignee: Knut Radloff CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2001-10-11 14:19 EDT by Lynne Kues CLA
Modified: 2002-01-15 16:06 EST (History)
2 users (show)

See Also:


Attachments
Same as comment from 2001-12-11 17:56. May be easier to read and update. (3.06 KB, text/plain)
2001-12-11 17:58 EST, Knut Radloff CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Lynne Kues CLA 2001-10-11 14:19:52 EDT
Wasn't a requirement, but McQ feels it should be on the new features list.  Low priority at this point.

NOTES:
	CM (10/2/2001 1:53:32 PM)
		Also: 1G9X6KJ: SWT:WIN2000 - Word wrapping does not work in StyledWidget

	CM (10/2/2001 1:53:38 PM)
		From Eclipse Corner:
			From: "Sharon" <sharon_dagan@il.ibm.com>
			Newsgroups: eclipse.tools
			Sent: Tuesday, September 18, 2001 6:21 PM
			Subject: Can StyledText widget wrap lines?

			> I've noticed that StyledText cannot wrap lines - if this is indeed the
			> case, I'd like to make that into a feature request.
			> 
			> -Sharon

		I guess users are starting to ask for this.
		The work to do word wrap was done for printing.
		Not sure how hard it would be to translate that over to having a WORD_WRAP style for the widget...
Comment 1 Lynne Kues CLA 2001-11-19 11:01:43 EST
We should investigate doing this to see what it will take.  
Comment 2 Carolyn MacLeod CLA 2001-11-19 14:32:33 EST
In our product, we use the SourceViewer (StyledText widget) to edit/view
SQL statements.   We use the SourceViewer for the syntax highlighting and
code assist
support.   A SourceViewer is used to display individual SQL clauses for a
SQL statement.   There can be anywhere from 1 to 5 clauses per statement.
Each
clause editor uses a SourceViewer.  In the screen capture below is an
example of a statement with 5 clause editors.   We have to have individual
clause editors
because we support non-standard SQL.   If the user were to enter his entire
statement in one editor, we wouldn't be able to parse it back out to the
individual clauses because of this non-standard support.   The example
below is very simplistic.   Our customers in reality have very long
clauses.   By default, we build clauses for our customers based on records
that are built from their SQL table(s).   They can have 1..many columns in
their table so the clauses that we build for them can be very long.
Sometimes we add tabs and carriage return line feeds but for the most part
the clauses are one long text string.  We would like the viewer that we are
using to wrap the text based on the size of the viewer.   As you can see,
there is not much screen real estate so we don't want to add  horizontal
scroll bars to each clause editor and we don't want our users to have to
scroll to horizontally to see their data.   This is why it is very
important for us to have the StyledText widget wrap lines.   We would like
to see the priority for this PR changed from a 3 to a 2.
Comment 3 Knut Radloff CLA 2001-12-11 17:56:40 EST
Function
========
Split lines after spaces (i.e., wrap words). If the last remaining word on the 
line does not fit entirely wrap character based, i.e. split the word.


General Concepts
================
ContentWidthCache:
Can be used to wrap lines. Wherever ContentWidthCache.calculate is called the 
line wrapping needs to be recalculated.
Note: During resize all wrapped lines need to be reset and those that are 
visible need to be recalculated. This is different from the normal use where 
lines are only reset when they (or their styles) have changed.

StyledTextContent:
Create a wrapper that works with visual line indices.
 getOffsetAtLine(visualLine)
 visualLine getLineAtOffset()
 String getLine(visualLine)
 numVisualLines getLineCount()

Note that the original, wrapped, StyledTextContent still needs to be accessible 
for use by StyledText API (e.g., getLineCount).


Changes to StyledText
=====================
(ok) means that something is trivial or needs no change to support word wrap. 
API methods should eventually lead to one of the internal methods. 

Internal Methods
----------------
computeSize:
 Compute preferred size to show as many wrapped lines as possible. Maximum 
width == screen width followed by maximum height == screen height.

handlePaint:
 topIndex is visual, content wrapper returns visual lines for visual indices
handlePaint->drawLine->getLineBackgroundData:
 Called with visual offset & line. same as getLineStyleData below
handlePaint->drawLine->drawLineSelectionBackground:
 Change line break selection

showBidiCaret:
 Ok if content.getLineAtOffset/content.getOffsetAtLine return data based on 
visual (wrapped) lines. Will create a bidi object using the wrapped line.
showBidiCaret->showLocation:
 Ok when called with visual line indices and assuming that the top index and 
bottom index represent visual lines.
showBidiCaret->setBidiCaretLocation:
 Similar to showBidiCaret.

showCaret->getXAtOffset->textWidth->getLineStyleData:
 Called with visual line data, needs to send an event using logical line. I.e., 
needs to  convert lineOffset, lineText back to logical. Also needs to (should, 
really - we may work with leaving bogus styles in the visual line) chop off 
styles that aren't on the requested visual line.
showCaret->setCaretLocation:
 Ok, gets called with visual line


API Methods
-----------
No op:
getHorizontalIndex (return 0), getHorizontalPixel (return 0), setHorizontalIndex

getLineAtOffset
 Return logical line

getLineBackground
 Return logical line

getLocationAtOffset
 Change to use visual line.

getOffsetAtLocation->getOffsetAtX->getLineStyleData:
 See textWidth above.

redraw (ok)

redrawRange->internalRedrawRange (ok)

replaceTextRange->handleTextChanging:
 change redraw
replaceTextRange->handleTextChanged->claimBottomFreeSpace-
>setVerticalScrollOffset (ok)

setText->handleTextSet->reset
 ->calculateContentWidth:
   calculate line wrap, interact with StyledTextContent wrapper
 ->handlePaint (s.a.)

showSelection->showOffset:
 ->getXAtOffset->textWidth (s.a.)
 ->showLocation (s.a.)

Comment 4 Knut Radloff CLA 2001-12-11 17:58:04 EST
Created attachment 167 [details]
Same as comment from 2001-12-11 17:56. May be easier to read and update.
Comment 5 Knut Radloff CLA 2001-12-18 11:56:10 EST
Keyboard navigation:
We solely rely on the caret offset to position the caret on a line in the 
document.
This presents a problem in word wrap mode because the end offset of a visual 
line is the same as the start offset of the next visual line. Therefore, when 
the caret is at the end of a visual line/start of the next visual line the 
usual content.getLineAtOffset can not be used to determine the actual visual 
line the caret is on. Likewise, the caret positioning code does not have enough 
information to place the caret on the correct visual line.
There are a few options to deal with this while only introducing localized code 
changes:
1. Pass the desired line to showCaret:
The desired line is unknown in the line change example because moving down 
one line from the end of visual line 0 (e.g., offset 2) is the same
as moving down one line from the start of visual line 1 (e.g., offset 2).
A fix would be to compare the caret position. However this would need to be
done for the line change case and for the line start/end case.

2. Passing the previous caret offset to showCaret:
This does not help because moving to the line end from offset 0 results in 
the same state as moving one line down from offset 0. Yet, the desired line
is 0 in the first case and 1 in the line down case.

3. Use the caret pixel location to determine the desired line:
The caret location would need to be checked in many navigation methods and then 
the desired line would need to be passed to showCaret. In addition mouse caret 
position changes would have to handled differently because they do not call 
showCaret.

Another option is to store the caret line index in addition to the caret 
offset. This does require a new field but is otherwise straightforward with the 
least amount of code changes in the navigation methods.
Whenever the caret offset is changed the desired caret line is known or can be 
calculated using the previous caret line. The caret line can then be used in 
showCaret and setCaretLocation to override the line returned by 
content.getLineAtOffset. The required code changes are fairly localized. 
There's a small risk to this change in that the caret line may not be set 
correctly. However, this shouldn't be a problem with sufficient testing.
Comment 6 Knut Radloff CLA 2001-12-20 16:01:21 EST
Enabling word wrap during widget startup:
Currently, when word wrap is enabled while the widget hierarchy is being 
initialized (i.e., the client area width is 0) all lines are wrapped to the 
first character only to be rewrapped to the client area width when the widget 
is first made visible.
There has to be *some* initial state because the WrappedContent has to answer 
valid values for the implemented StyledTextContent API. We could deal with this 
by testing whether the client area width is 0 and if it is forward all 
StyledTextContent API calls to the real (logical) StyledTextContent 
implementor. This would mean that the initial state is "infinite client area 
width". 
Comment 7 Knut Radloff CLA 2002-01-09 15:34:09 EST
Changed setTopIndex/getTopIndex to take/answer the logical line index. If there 
is more than one visual line at a logical line index the first visual line 
(i.e., the first of a series of wrapped lines) is made the top line.
Comment 8 Knut Radloff CLA 2002-01-09 17:22:17 EST
Preferred Size in Word Wrap Mode
================================
StyledText.computeSize now wraps lines based on the specified width hint or the 
StyledText.DEFAULT_WIDTH if no hint is specified. 
The resulting number of lines is the preferred height unless a height hint is 
specified or the widget is in SINGLE line mode.
Comment 9 Knut Radloff CLA 2002-01-15 16:06:25 EST
Released word wrap support into HEAD. Use the SWT.WRAP creation style or the 
new setWordWrap API to activate.
We did not do a performance pass. The resize performance is a little slow when 
word wrap is on and a large number of lines are wrapped. Resize is very slow on 
bidi platforms (Hebrew, Arabic) in the same scenario.
One optimization to speed up resizing the widget smaller would be to store not 
only the length of a line but also its width. We could then compare the line 
width against the new wrap width and skip lines that still fit. This would 
improve the performance when only a few lines actually need rewrapping based on 
a new, smaller widget width.