Refactor painting of editor gutter

This commit is contained in:
eelke 2022-04-10 10:29:40 +02:00
parent e082a5731d
commit a5563949e5
7 changed files with 188 additions and 85 deletions

View file

@ -86,8 +86,8 @@ void CodeEditor::onTextChanged()
void CodeEditor::addErrorMarker(int position, int length)
{
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::red).lighter(160);
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::red).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setFontItalic(true);
selection.cursor = textCursor();
@ -120,45 +120,17 @@ void CodeEditor::updateExtraSelections()
setExtraSelections(extraSelections);
}
void CodeEditor::gutterAreaPaintEvent(QPaintEvent *event)
void CodeEditor::drawGutterErrorMarker(QPainter &painter, int top)
{
QPainter painter(gutterArea);
painter.fillRect(event->rect(), Qt::lightGray);
int s = fontMetrics().height() - 8;
painter.setBrush(QBrush(Qt::red));
painter.drawEllipse(4, top + 4, s, s);
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
int bottom = top + (int) blockBoundingRect(block).height();
}
// We will now loop through all visible lines and paint the line numbers in the
// extra area for each line. Notice that in a plain text edit each line will
// consist of one QTextBlock; though, if line wrapping is enabled, a line may span
// several rows in the text edit's viewport.
//
// We get the top and bottom y-coordinate of the first text block, and adjust these
// values by the height of the current text block in each iteration in the loop.
while (block.isValid() && top <= event->rect().bottom())
{
if (block.isVisible() && bottom >= event->rect().top())
{
QString number = QString::number(blockNumber + 1);
painter.setPen(Qt::black);
painter.drawText(0, top, gutterArea->width(), fontMetrics().height(),
Qt::AlignRight, number);
if (errorLines.count(blockNumber) > 0)
{
int s = fontMetrics().height() - 8;
painter.setBrush(QBrush(Qt::red));
painter.drawEllipse(4, top + 4, s, s);
}
}
block = block.next();
top = bottom;
bottom = top + (int) blockBoundingRect(block).height();
++blockNumber;
}
bool CodeEditor::lineHasError(int blockNumber) const
{
return errorLines.count(blockNumber) > 0;
}
void CodeEditor::keyPressEvent(QKeyEvent *e)
@ -194,6 +166,8 @@ void CodeEditor::keyPressEvent(QKeyEvent *e)
QPlainTextEdit::keyPressEvent(e);
}
bool CodeEditor::indentSelection(bool indent)
{
auto cursor = textCursor();
@ -222,63 +196,75 @@ bool CodeEditor::indentSelection(bool indent)
{
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
if (indent)
{
if (m_useTab)
cursor.insertText("\t");
else
cursor.insertText(QString(m_tabSize, ' '));
}
insertIndentation(cursor);
else
{
// remove tab if there is a tab
cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
const QString text = cursor.selectedText();
int index = 0;
int pos = 0;
while (pos < m_tabSize && index < text.length())
{
QChar c = text[index++];
if (c == ' ')
++pos;
else if (c == '\t')
pos = ((pos + m_tabSize) / m_tabSize) * m_tabSize;
else
{
--index;
break;
}
}
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
for (int i = 0; i < index; ++i)
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
}
removeIndentation(cursor);
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
}
cursor.endEditBlock();
cursor.setPosition(first_pos, QTextCursor::MoveAnchor);
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
while(cursor.block().blockNumber() < end_block)
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
makeSelection(cursor, first_pos, end_block);
setTextCursor(cursor);
return true;
}
void CodeEditor::insertIndentation(QTextCursor &cursor)
{
if (m_useTab)
cursor.insertText("\t");
else
cursor.insertText(QString(m_tabSize, ' '));
}
void CodeEditor::removeIndentation(QTextCursor &cursor)
{
// remove tab if there is a tab
cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
const QString text = cursor.selectedText();
int index = 0;
int pos = 0;
while (pos < m_tabSize && index < text.length())
{
QChar c = text[index++];
if (c == ' ')
++pos;
else if (c == '\t')
pos = ((pos + m_tabSize) / m_tabSize) * m_tabSize;
else
{
--index;
break;
}
}
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
for (int i = 0; i < index; ++i)
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
}
void CodeEditor::makeSelection(QTextCursor &cursor, int first_pos, int end_block)
{
cursor.setPosition(first_pos, QTextCursor::MoveAnchor);
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
while(cursor.block().blockNumber() < end_block)
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
}
void CodeEditor::setFont(const QFont &f)
{
QWidget::setFont(f);
auto orig_tab = m_tabSize;
m_tabSize = 0;
setTabSize(orig_tab);
QWidget::setFont(f);
auto orig_tab = m_tabSize;
m_tabSize = 0;
setTabSize(orig_tab);
}
void CodeEditor::setTabSize(int chars)
{
m_tabSize = chars;
int pixels = fontMetrics().horizontalAdvance(QString(chars, '0'));
this->setTabStopDistance(pixels);
m_tabSize = chars;
int pixels = fontMetrics().horizontalAdvance(QString(chars, '0'));
this->setTabStopDistance(pixels);
}