diff options
author | David Schulz <david.schulz@qt.io> | 2022-11-23 14:59:59 +0100 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2022-11-25 05:24:47 +0000 |
commit | c10600134f93ec84bf825197b8d5dbf0ec6af359 (patch) | |
tree | 1abc333ec6dc9b5f77497161451536a4dc07d992 /src/plugins/texteditor | |
parent | 3d05611726686e826ddeaf435d2c61628576cdad (diff) | |
download | qt-creator-c10600134f93ec84bf825197b8d5dbf0ec6af359.tar.gz |
Editor: do not paint rounded corners in editor overlay
Calculating and optimizing a painter path with rounded corners can
freeze or under certain circumstances even crash Qt Creator. Since we
are using a less rounded design nowadays anyways we can also cut this
overhead.
Fixes: QTCREATORBUG-21056
Change-Id: I9cd5eaea00ab040d529f51ea6d2f337dd0a1f6b8
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Diffstat (limited to 'src/plugins/texteditor')
-rw-r--r-- | src/plugins/texteditor/texteditoroverlay.cpp | 239 | ||||
-rw-r--r-- | src/plugins/texteditor/texteditoroverlay.h | 3 |
2 files changed, 91 insertions, 151 deletions
diff --git a/src/plugins/texteditor/texteditoroverlay.cpp b/src/plugins/texteditor/texteditoroverlay.cpp index 919a51610e..6248e1623b 100644 --- a/src/plugins/texteditor/texteditoroverlay.cpp +++ b/src/plugins/texteditor/texteditoroverlay.cpp @@ -16,11 +16,12 @@ using namespace TextEditor; using namespace TextEditor::Internal; +constexpr int borderWidth = 1; + TextEditorOverlay::TextEditorOverlay(TextEditorWidget *editor) : QObject(editor), m_visible(false), m_alpha(true), - m_borderWidth(1), m_dropShadowWidth(2), m_firstSelectionOriginalBegin(-1), m_editor(editor), @@ -112,19 +113,15 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co QTextDocument *document = m_editor->document(); if (m_editor->blockBoundingGeometry(begin.block()).translated(offset).top() > clip.bottom() + 10 - || m_editor->blockBoundingGeometry(end.block()).translated(offset).bottom() < clip.top() - 10 - ) + || m_editor->blockBoundingGeometry(end.block()).translated(offset).bottom() + < clip.top() - 10) { return QPainterPath(); // nothing of the selection is visible - + } QTextBlock block = begin.block(); - if (block.blockNumber() < m_editor->firstVisibleBlock().blockNumber() - 4) - block = m_editor->document()->findBlockByNumber(m_editor->firstVisibleBlock().blockNumber() - 4); - - bool inSelection = false; - - QVector<QRectF> selection; + if (block.blockNumber() < m_editor->firstVisibleBlock().blockNumber() - 1) + block = document->findBlockByNumber(m_editor->firstVisibleBlock().blockNumber() - 1); if (begin.position() == end.position()) { // special case empty selections @@ -133,160 +130,108 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co int pos = begin.position() - begin.block().position(); QTextLine line = blockLayout->lineForTextPosition(pos); QRectF lineRect = line.naturalTextRect(); + lineRect = lineRect.translated(blockGeometry.topLeft()); int x = line.cursorToX(pos); - lineRect.setLeft(x - m_borderWidth); - lineRect.setRight(x + m_borderWidth); - selection += lineRect.translated(blockGeometry.topLeft()); - } else { - for (; block.isValid() && block.blockNumber() <= end.blockNumber(); block = block.next()) { - if (! block.isVisible()) - continue; - - const QRectF blockGeometry = m_editor->blockBoundingGeometry(block); - QTextLayout *blockLayout = block.layout(); - - QTextLine line = blockLayout->lineAt(0); - bool firstOrLastBlock = false; - - int beginChar = 0; - if (!inSelection) { - if (block == begin.block()) { - beginChar = begin.positionInBlock(); - line = blockLayout->lineForTextPosition(beginChar); - firstOrLastBlock = true; - } - inSelection = true; - } else { -// while (beginChar < block.length() && document->characterAt(block.position() + beginChar).isSpace()) -// ++beginChar; -// if (beginChar == block.length()) -// beginChar = 0; - } - - int lastLine = blockLayout->lineCount()-1; - int endChar = -1; - if (block == end.block()) { - endChar = end.positionInBlock(); - lastLine = blockLayout->lineForTextPosition(endChar).lineNumber(); - inSelection = false; - firstOrLastBlock = true; - } else { - endChar = block.length(); - while (endChar > beginChar && document->characterAt(block.position() + endChar - 1).isSpace()) - --endChar; - } - - QRectF lineRect = line.naturalTextRect(); - if (beginChar < endChar) { - lineRect.setLeft(line.cursorToX(beginChar)); - if (line.lineNumber() == lastLine) - lineRect.setRight(line.cursorToX(endChar)); - selection += lineRect.translated(blockGeometry.topLeft()); - - for (int lineIndex = line.lineNumber()+1; lineIndex <= lastLine; ++lineIndex) { - line = blockLayout->lineAt(lineIndex); - lineRect = line.naturalTextRect(); - if (lineIndex == lastLine) - lineRect.setRight(line.cursorToX(endChar)); - selection += lineRect.translated(blockGeometry.topLeft()); - } - } else { // empty lines - const int emptyLineSelectionSize = 16; - if (!firstOrLastBlock && !selection.isEmpty()) { // middle - lineRect.setLeft(selection.last().left()); - } else if (inSelection) { // first line - lineRect.setLeft(line.cursorToX(beginChar)); - } else { // last line - if (endChar == 0) - break; - lineRect.setLeft(line.cursorToX(endChar) - emptyLineSelectionSize); - } - lineRect.setRight(lineRect.left() + emptyLineSelectionSize); - selection += lineRect.translated(blockGeometry.topLeft()); - } - - if (!inSelection) - break; - - if (blockGeometry.translated(offset).y() > 2*viewportRect.height()) - break; - } + lineRect.setLeft(x - borderWidth); + lineRect.setRight(x + borderWidth); + QPainterPath path; + path.addRect(lineRect); + return path; } + QPointF top; // *------| + QPointF left; // *---| | + QPointF right; // | |---* + QPointF bottom; // |------* - if (selection.isEmpty()) - return QPainterPath(); + for (; block.isValid() && block.blockNumber() <= end.blockNumber(); block = block.next()) { + if (!block.isVisible()) + continue; - QVector<QPointF> points; + const QRectF blockGeometry = m_editor->blockBoundingGeometry(block); + QTextLayout *blockLayout = block.layout(); - const int margin = m_borderWidth/2; - const int extra = 0; + int firstLine = 0; + QTextLine line = blockLayout->lineAt(firstLine); - const QRectF &firstSelection = selection.at(0); - points += (firstSelection.topLeft() + firstSelection.topRight()) / 2 + QPointF(0, -margin); - points += firstSelection.topRight() + QPointF(margin+1, -margin); - points += firstSelection.bottomRight() + QPointF(margin+1, 0); + int beginChar = 0; + if (block == begin.block()) { + beginChar = begin.positionInBlock(); + line = blockLayout->lineForTextPosition(beginChar); + firstLine = line.lineNumber(); + const int lineEnd = line.textStart() + line.textLength(); + if (beginChar == lineEnd) + continue; + } + + int lastLine = blockLayout->lineCount() - 1; + int endChar = -1; + if (block == end.block()) { + endChar = end.positionInBlock(); + lastLine = blockLayout->lineForTextPosition(endChar).lineNumber(); + if (endChar == beginChar) + break; // Do not expand overlay to empty selection at end + } else { + endChar = block.length(); + while (endChar > beginChar + && document->characterAt(block.position() + endChar - 1).isSpace()) + --endChar; + } - const int count = selection.count(); - for (int i = 1; i < count-1; ++i) { - qreal x = std::max({selection.at(i - 1).right(), - selection.at(i).right(), - selection.at(i + 1).right()}) - + margin; + for (int i = firstLine; i <= lastLine; ++i) { + line = blockLayout->lineAt(i); + QRectF lineRect = line.naturalTextRect(); + if (i == firstLine && beginChar > 0) + lineRect.setLeft(line.cursorToX(beginChar)); + if (line.lineNumber() == lastLine) + lineRect.setRight(line.cursorToX(endChar)); + lineRect = lineRect.translated(blockGeometry.topLeft()); + if (top.isNull()) + top = lineRect.topLeft(); + else if (left.isNull()) + left = lineRect.topLeft(); + else + left.setX(std::min(left.x(), lineRect.left())); + if (i == lastLine && block == end.block() && lineRect.right() <= right.x()) + bottom = lineRect.bottomRight(); + else + right = {std::max(lineRect.right(), right.x()), lineRect.bottom()}; + } - points += QPointF(x+1, selection.at(i).top()); - points += QPointF(x+1, selection.at(i).bottom()); + if (blockGeometry.translated(offset).y() > 2 * viewportRect.height()) + break; } - const QRectF &lastSelection = selection.at(count-1); - points += lastSelection.topRight() + QPointF(margin+1, 0); - points += lastSelection.bottomRight() + QPointF(margin+1, margin+extra); - points += lastSelection.bottomLeft() + QPointF(-margin, margin+extra); - points += lastSelection.topLeft() + QPointF(-margin, 0); + if (top.isNull()) + return {}; - for (int i = count-2; i > 0; --i) { - qreal x = std::min({selection.at(i - 1).left(), - selection.at(i).left(), - selection.at(i + 1).left()}) - - margin; + if (bottom.isNull()) + bottom = right; - points += QPointF(x, selection.at(i).bottom()+extra); - points += QPointF(x, selection.at(i).top()); - } + if (left.isNull()) + left = top; - points += firstSelection.bottomLeft() + QPointF(-margin, extra); - points += firstSelection.topLeft() + QPointF(-margin, -margin); + if (right.isNull()) + right = bottom; + constexpr QPointF marginOffset = {borderWidth, borderWidth}; + right += marginOffset; + bottom += marginOffset; QPainterPath path; - const int corner = 4; - path.moveTo(points.at(0)); - points += points.at(0); - QPointF previous = points.at(0); - for (int i = 1; i < points.size(); ++i) { - QPointF point = points.at(i); - if (point.y() == previous.y() && qAbs(point.x() - previous.x()) > 2*corner) { - QPointF tmp = QPointF(previous.x() + corner * ((point.x() > previous.x())?1:-1), previous.y()); - path.quadTo(previous, tmp); - previous = tmp; - i--; - continue; - } else if (point.x() == previous.x() && qAbs(point.y() - previous.y()) > 2*corner) { - QPointF tmp = QPointF(previous.x(), previous.y() + corner * ((point.y() > previous.y())?1:-1)); - path.quadTo(previous, tmp); - previous = tmp; - i--; - continue; - } - + path.moveTo(top); + path.lineTo(right.x(), top.y()); + path.lineTo(right); + path.lineTo(bottom.x(), right.y()); + path.lineTo(bottom); + path.lineTo(left.x(), bottom.y()); + path.lineTo(left); + path.lineTo(top.x(), left.y()); + path.lineTo(top); - QPointF target = (previous + point) / 2; - path.quadTo(previous, target); - previous = points.at(i); - } path.closeSubpath(); path.translate(offset); - return path.simplified(); + return path; } void TextEditorOverlay::paintSelection(QPainter *painter, @@ -310,7 +255,7 @@ void TextEditorOverlay::paintSelection(QPainter *painter, QColor penColor = fg; if (m_alpha) penColor.setAlpha(220); - QPen pen(penColor, m_borderWidth); + QPen pen(penColor, borderWidth); painter->translate(-.5, -.5); QRectF pathRect = path.controlPointRect(); @@ -332,8 +277,6 @@ void TextEditorOverlay::paintSelection(QPainter *painter, painter->setBrush(QBrush(linearGrad)); } - painter->setRenderHint(QPainter::Antialiasing); - if (selection.m_dropShadow) { painter->save(); QPainterPath shadow = path; @@ -345,7 +288,7 @@ void TextEditorOverlay::paintSelection(QPainter *painter, painter->restore(); } - pen.setJoinStyle(Qt::RoundJoin); + pen.setJoinStyle(Qt::MiterJoin); painter->setPen(pen); painter->drawPath(path); painter->restore(); diff --git a/src/plugins/texteditor/texteditoroverlay.h b/src/plugins/texteditor/texteditoroverlay.h index 4cd8aa5bb9..d0946c636a 100644 --- a/src/plugins/texteditor/texteditoroverlay.h +++ b/src/plugins/texteditor/texteditoroverlay.h @@ -45,8 +45,6 @@ public: inline void hide() { setVisible(false); } inline void show() { setVisible(true); } - void setBorderWidth(int bw) {m_borderWidth = bw; } - void update(); void setAlpha(bool enabled) { m_alpha = enabled; } @@ -83,7 +81,6 @@ private: bool m_visible; bool m_alpha; - int m_borderWidth; int m_dropShadowWidth; int m_firstSelectionOriginalBegin; TextEditorWidget *m_editor; |