Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Standard Widget Toolkit (SWT) » StyledText location at offset on word wrap problem
StyledText location at offset on word wrap problem [message #897364] Mon, 23 July 2012 19:14 Go to next message
Andi Thomas is currently offline Andi Thomas
Messages: 38
Registered: July 2009
Member
I've logged an update to bug 281747 for a problem I can't figure out how
to code around. https://bugs.eclipse.org/bugs/show_bug.cgi?id=281747

The gist of it is that I want to get the location of a word in a
StyledText. This works most of the time, but when the word is the first
word on a newly word-wrapped line, I get the wrong position.

This method org.eclipse.swt.custom.StyledText.getPointAtOffset(int)
seems to be the one misbehaving because the caretAlignment value is set
to PREVIOUS_OFFSET_TRAILING. This makes it return the trailing position
of the previous offset rather than the leading position of the provided
offset. When a word is the first on a wrapped line, this is the location
of the space preceding the word - which is on the previous line.

Clearly, StyledText is being used all over the place and I would have
thought this would have been picked up before.

So, the questions for the group are:
- What am I doing wrong?
- How should I be going about this?
- What am I missing that would make this work?
Re: StyledText location at offset on word wrap problem [message #899868 is a reply to message #897364] Thu, 02 August 2012 16:34 Go to previous messageGo to next message
Andi Thomas is currently offline Andi Thomas
Messages: 38
Registered: July 2009
Member
The suggestion on the bug is to use a packaged protected method. I'm not
too happy about that so I've come up with another workaround.

Since the getLocationAtOffset(int) method actually returns the trailing
position of the previous offset location, I'm getting the location of
the next offset and then subtracting the width of the character at the
offset.

Its a hacky workaround for what is clearly buggy behavior.

Here's the code if anyone is interested.
private Point getLocationAtOffset(final StyledText text, final int
offset) {
if (offset == text.getCharCount())
return text.getLocationAtOffset(offset);

Rectangle bounds = text.getTextBounds(offset, offset);
Point point = text.getLocationAtOffset(offset+1);
point.x = point.x - bounds.width;
return point;
}

If someone else figured out a more elegant solution, I'd love to hear it.


On 7/23/12 3:14 PM, Andi Thomas wrote:
> I've logged an update to bug 281747 for a problem I can't figure out how
> to code around. https://bugs.eclipse.org/bugs/show_bug.cgi?id=281747
>
> The gist of it is that I want to get the location of a word in a
> StyledText. This works most of the time, but when the word is the first
> word on a newly word-wrapped line, I get the wrong position.
>
> This method org.eclipse.swt.custom.StyledText.getPointAtOffset(int)
> seems to be the one misbehaving because the caretAlignment value is set
> to PREVIOUS_OFFSET_TRAILING. This makes it return the trailing position
> of the previous offset rather than the leading position of the provided
> offset. When a word is the first on a wrapped line, this is the location
> of the space preceding the word - which is on the previous line.
>
> Clearly, StyledText is being used all over the place and I would have
> thought this would have been picked up before.
>
> So, the questions for the group are:
> - What am I doing wrong?
> - How should I be going about this?
> - What am I missing that would make this work?
Re: StyledText location at offset on word wrap problem [message #900168 is a reply to message #899868] Sat, 04 August 2012 23:09 Go to previous messageGo to next message
Doug M is currently offline Doug M
Messages: 18
Registered: December 2009
Junior Member
Hi Andi. I feel your pain. My project makes extensive use of StyledText and Text. By now I have a dozen patches and listeners installed both to extend their capacities to those I need, and to compensate for wonky behavior.

I ran into the corresponding problem with setCaretOffset. When I want the caret placed before the first word of a wrapped line, it is rather placed after the trailing space of the previous wrapped line. Worse, with certain fonts, the caret disappears entirely in that position.

In this case, PREVIOUS_OFFSET_TRAILING is hard coded into the public method.

The best solution I've found for this and similar problems is to *horrors* access the package protected methods. I understand that this approach is inelegant and subject to breaking. But it has the advantages of being both more correct and more efficient (which matters) than various hacks cobbled together out of the public methods. For me, those advantages outweigh the disadvantages.

In case you have never done such a thing, there's an easier way than reflection. Just create package org.eclipse.SWT.custom in your project and throw the patching class into it. It will have access to the package protected methods.

Attached is part of my patch class for StyledText. One needs to use PStyledText everywhere StyledText was used. For your situation, to be completely safe from side effects, you would only need to make a specially named myGetLocationAtOffset that sets and restores caretAlignment bracketting a call to getLocationAtOffset. Or be more daring and correct the behavior of getLocationAtOffset in PStyledText with an override.

If you are using opaque classes that themselves use StyledText, a more powerful and fragile technique is to place an altered complete copy of StyledText in your patch package, and place class file in front of SWT in CLASSPATH.

Most likely the Java purists will behead me for such advice. But sometimes you just gotta get the job done.

Best Regards,
Doug

/*
 * @author doug
 * 
 * Guess it had to happen eventually. Finally hit an un-work-aroundable bug.
 * 
 * In this case, all the methods for setting the caret at the beginning of a line invariably
 * place it at the end of the previous line. SetCaretOffset is replaced here with a version that
 * uses the OFFSET_LEADING alignment.
 * 
 * Use PStyledText wherever StyledText was used. This technique works only for classes that
 * the project uses directly. Corrections to intermediate classes that use StyledText require 
 * replacing the defective class entirely.
 */
package org.eclipse.swt.custom;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.TextLayout;
import org.eclipse.swt.widgets.Composite;

public class PStyledText extends StyledText {
	public PStyledText(Composite parent, int style) {
		super(parent, style);
	}

	@Override
	public void setCaretOffset(int offset) {
		checkWidget();
		int length = getCharCount();
		if (length > 0 && offset != caretOffset) {
			if (offset < 0) {
				offset = 0;
			} else if (offset > length) {
				offset = length;
			} else {
				if (isLineDelimiter(offset)) {
					// offset is inside a multi byte line delimiter. This is an 
					// illegal operation and an exception is thrown. Fixes 1GDKK3R
					SWT.error(SWT.ERROR_INVALID_ARGUMENT);
				}
			}
//*****			setCaretOffset(offset, PREVIOUS_OFFSET_TRAILING);
			setCaretOffset(offset, OFFSET_LEADING);
			// clear the selection if the caret is moved.
			// don't notify listeners about the selection change.
			if (blockSelection) {
				clearBlockSelection(true, false);
			} else {
				clearSelection(false);
			}
		}
		setCaretLocation();
	}

	public int getOffsetAtYLimit(int y) {
		/* 20120713 dmm. Derived from getOffsetAtPoint. Find the character index at the end of the line that
		 * falls completely within y.
		 */
		/* if (inTextOnly && y + getVerticalScrollOffset() < 0 || x + horizontalScrollOffset < 0) {
		 * return -1;
		 * }
		 */
		int lineIndex = getLineIndex(y);
		int lineOffset = content.getOffsetAtLine(lineIndex);
		TextLayout layout = renderer.getTextLayout(lineIndex);
		y -= getLinePixel(lineIndex);
		
		int li = layout.getLineCount()-1;
		while (li >= 0) {
			Rectangle rect = layout.getLineBounds(li);

			if (rect.y + rect.height <= y)
				break;
			li--;
		}
		int[] lineOffsets = layout.getLineOffsets();
		renderer.disposeTextLayout(layout);
		return lineOffsets[li + 1] + lineOffset;
	}
}

Re: StyledText location at offset on word wrap problem [message #900306 is a reply to message #900168] Mon, 06 August 2012 12:38 Go to previous messageGo to next message
Andi Thomas is currently offline Andi Thomas
Messages: 38
Registered: July 2009
Member
Thanks immensely for this reply Doug. Just knowing that I'm not the only
one going crazy with this widget is a great comfort.

I was hoping to avoid extending StyledText in the same package, but I
fear it may be inevitable.
Re: StyledText location at offset on word wrap problem [message #901309 is a reply to message #900306] Fri, 10 August 2012 17:15 Go to previous message
Doug M is currently offline Doug M
Messages: 18
Registered: December 2009
Junior Member
Hi Andi. Please contact me directly at info2@cssw.biz. Thanks. Doug
Previous Topic:Access to pixel data of SWT Image from native libary
Next Topic:Selection Listener on TabFolder executing five times on each click?
Goto Forum:
  


Current Time: Fri Oct 31 16:36:12 GMT 2014

Powered by FUDForum. Page generated in 0.01789 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software