diff options
author | Conrad Groth | 2020-03-15 00:29:16 +0000 |
---|---|---|
committer | Mickael Istria | 2020-03-30 19:45:20 +0000 |
commit | 2abb22fa4db3f7c87fc38290f55d0d8c4d4a4465 (patch) | |
tree | 6693bf9400d6b4c177c8edf0167bc1535b7c4785 | |
parent | 235c551e987a8901900fccf6cc362cf3f0529bde (diff) | |
download | eclipse.platform.swt-2abb22fa4db3f7c87fc38290f55d0d8c4d4a4465.tar.gz eclipse.platform.swt-2abb22fa4db3f7c87fc38290f55d0d8c4d4a4465.tar.xz eclipse.platform.swt-2abb22fa4db3f7c87fc38290f55d0d8c4d4a4465.zip |
Bug 168557 - [StyledText] StyledText is very slow with long string +
word wrap
Avoid the synchronous calculation of the line height in the UI thread
and let the idle job do it. For scrolling and resizing, especially when
huge amounts of line heights are not yet calculated, the average line
height is good enough.
Change-Id: I4a7a25d78597e08efc379b53ba7985adccab0eec
Signed-off-by: Conrad Groth <info@conrad-groth.de>
4 files changed, 98 insertions, 39 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java index 1a1f1fad94..12fd00148f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledText.java @@ -1587,30 +1587,30 @@ void calculateTopIndex(int delta) { int lineCount = content.getLineCount(); while (lineIndex < lineCount) { if (delta <= 0) break; - delta -= renderer.getLineHeight(lineIndex++); + delta -= renderer.getCachedLineHeight(lineIndex++); } - if (lineIndex < lineCount && -delta + renderer.getLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) { + if (lineIndex < lineCount && -delta + renderer.getCachedLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) { topIndex = lineIndex; topIndexY = -delta; } else { topIndex = lineIndex - 1; - topIndexY = -renderer.getLineHeight(topIndex) - delta; + topIndexY = -renderer.getCachedLineHeight(topIndex) - delta; } } else { delta -= topIndexY; int lineIndex = topIndex; while (lineIndex > 0) { - int lineHeight = renderer.getLineHeight(lineIndex - 1); + int lineHeight = renderer.getCachedLineHeight(lineIndex - 1); if (delta + lineHeight > 0) break; delta += lineHeight; lineIndex--; } - if (lineIndex == 0 || -delta + renderer.getLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) { + if (lineIndex == 0 || -delta + renderer.getCachedLineHeight(lineIndex) <= clientAreaHeight - topMargin - bottomMargin) { topIndex = lineIndex; topIndexY = - delta; } else { topIndex = lineIndex - 1; - topIndexY = - renderer.getLineHeight(topIndex) - delta; + topIndexY = - renderer.getCachedLineHeight(topIndex) - delta; } } } @@ -5397,7 +5397,7 @@ int getVerticalScrollOffset() { renderer.calculate(0, topIndex); int height = 0; for (int i = 0; i < topIndex; i++) { - height += renderer.getLineHeight(i); + height += renderer.getCachedLineHeight(i); } height -= topIndexY; verticalScrollOffset = height; @@ -8227,7 +8227,11 @@ boolean scrollVertical(int pixels, boolean adjustScrollBar) { calculateTopIndex(pixels); super.redraw(); } - setCaretLocation(); + Caret caret = getCaret(); + if (caret != null) { + Point caretLocation = caret.getLocation(); + setCaretLocation(new Point(caretLocation.x, caretLocation.y - pixels), getCaretDirection()); + } return true; } void scrollText(int srcY, int destY) { @@ -8715,35 +8719,11 @@ void setCaretLocation(final Point location, int direction) { getStyleRangeAtOffset(caretOffset) : getStyleRangeAtOffset(content.getCharCount() - 1)) : // caret after last char: use last char style null; - final int caretLine = getCaretLine(); - - int graphicalLineHeight = getLineHeight(); - final int lineStartOffset = getOffsetAtLine(caretLine); - int graphicalLineFirstOffset = lineStartOffset; - final int lineEndOffset = lineStartOffset + getLine(caretLine).length(); - int graphicalLineLastOffset = lineEndOffset; - if (caretLine < getLineCount() && renderer.getLineHeight(caretLine) != getLineHeight()) { // word wrap, metrics, styles... - graphicalLineHeight = getLineHeight(caretOffset); - final Rectangle characterBounds = getBoundsAtOffset(caretOffset); - graphicalLineFirstOffset = getOffsetAtPoint(new Point(leftMargin, characterBounds.y)); - graphicalLineLastOffset = getOffsetAtPoint(new Point(leftMargin, characterBounds.y + graphicalLineHeight)) - 1; - if (graphicalLineLastOffset < graphicalLineFirstOffset) { - graphicalLineLastOffset = getCharCount(); - } - } + int graphicalLineHeight = getLineHeight(caretOffset); int caretHeight = getLineHeight(); - boolean isTextAlignedAtBottom = true; - if (graphicalLineFirstOffset >= 0) { - for (StyleRange style : getStyleRanges(graphicalLineFirstOffset, graphicalLineLastOffset - graphicalLineFirstOffset)) { - isTextAlignedAtBottom &= ( - (style.font == null || Objects.equals(style.font, getFont())) && - style.rise >= 0 && - (style.metrics == null || style.metrics.descent <= 0) - ); - } - } - if (!isTextAlignedAtBottom || (styleAtOffset != null && styleAtOffset.isVariableHeight())) { + + if (styleAtOffset != null && styleAtOffset.isVariableHeight()) { if (isDefaultCaret) { direction = SWT.DEFAULT; caretHeight = graphicalLineHeight; @@ -8751,7 +8731,7 @@ void setCaretLocation(final Point location, int direction) { caretHeight = caret.getSize().y; } } - if (isTextAlignedAtBottom && caretHeight < graphicalLineHeight) { + if (caretHeight < graphicalLineHeight) { location.y += (graphicalLineHeight - caretHeight); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextRenderer.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextRenderer.java index 20eec0ab03..7a9d8de766 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextRenderer.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/StyledTextRenderer.java @@ -51,6 +51,8 @@ class StyledTextRenderer { LineInfo[] lines; int maxWidth; int maxWidthLineIndex; + float averageLineHeight; + int linesInAverageLineHeight; boolean idleRunning; /* Bullet */ @@ -297,6 +299,7 @@ void calculate(int startLine, int lineCount) { Rectangle rect = layout.getBounds(); line.width = rect.width + hTrim; line.height = rect.height; + averageLineHeight += (line.height - Math.round(averageLineHeight)) / ++linesInAverageLineHeight; disposeTextLayout(layout); } if (line.width > maxWidth) { @@ -544,6 +547,9 @@ int drawLine(int lineIndex, int paintX, int paintY, GC gc, Color widgetBackgroun int getBaseline() { return ascent; } +int getCachedLineHeight(int lineIndex) { + return getLineHeight(lineIndex, false); +} Font getFont(int style) { switch (style) { case SWT.BOLD: @@ -655,12 +661,19 @@ int getLineHeight() { return ascent + descent; } int getLineHeight(int lineIndex) { + return getLineHeight(lineIndex, true); +} +int getLineHeight(int lineIndex, boolean exact) { LineSizeInfo line = getLineSize(lineIndex); if (line.needsRecalculateHeight()) { - // here we are in "variable line height", the call of calculate which uses TextLayout can be very slow - // check if line can use the default line height. + // here we are in "variable line height", the call of calculate which uses TextLayout is very slow + // so use the average line height of all calculated lines when many heights are needed e.g. for scrolling. if (isVariableHeight(lineIndex)) { - calculate(lineIndex, 1); + if (exact) { + calculate(lineIndex, 1); + } else { + return Math.round(averageLineHeight); + } } else { line.height = getLineHeight() + getLineSpacing(lineIndex); } @@ -1327,6 +1340,12 @@ void reset(Set<Integer> lines) { getLineSize(line.intValue()).resetSize(); } } + if (linesInAverageLineHeight > resetLineCount) { + linesInAverageLineHeight -= resetLineCount; + } else { + linesInAverageLineHeight = 0; + averageLineHeight = 0.0f; + } if (lines.contains(Integer.valueOf(maxWidthLineIndex))) { maxWidth = 0; maxWidthLineIndex = -1; diff --git a/examples/org.eclipse.swt.snippets/previews/Snippet376.png b/examples/org.eclipse.swt.snippets/previews/Snippet376.png Binary files differnew file mode 100644 index 0000000000..388d213458 --- /dev/null +++ b/examples/org.eclipse.swt.snippets/previews/Snippet376.png diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet376.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet376.java new file mode 100644 index 0000000000..fe8447672b --- /dev/null +++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet376.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2020 Conrad Groth and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Conrad Groth - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.snippets; + +import org.eclipse.swt.*; +import org.eclipse.swt.custom.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +/** + * Example snippet: Styled Text Widget with 50000 lines of text and line wrap. + * + * For a list of all SWT example snippets see + * http://www.eclipse.org/swt/snippets/ + * + * @since 3.114 + */ +public class Snippet376 { + + private static final int LINES = 500000; + private static final int WORDS_PER_LINE = 10; + private static final String WORD = "123456789 "; + + public static void main(String[] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setBounds(10, 10, 300, 300); + shell.setLayout(new FillLayout()); + final StyledText text = new StyledText(shell, SWT.MULTI | SWT.V_SCROLL | SWT.WRAP); + + StringBuilder buffer = new StringBuilder(LINES * WORDS_PER_LINE * WORD.length() * 2); + for (int i = 0; i < LINES ; i++) { + buffer.append("Line " + i + ": "); + for (int j = 0; j < WORDS_PER_LINE; j++) { + buffer.append(WORD); + } + buffer.append("\n"); + } + + shell.open(); + text.setText(buffer.toString()); + + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + } +}
\ No newline at end of file |