Refactor painting of editor gutter
This commit is contained in:
parent
e082a5731d
commit
a5563949e5
7 changed files with 188 additions and 85 deletions
|
|
@ -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);
|
||||
|
||||
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,14 +196,29 @@ bool CodeEditor::indentSelection(bool indent)
|
|||
{
|
||||
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
||||
if (indent)
|
||||
{
|
||||
insertIndentation(cursor);
|
||||
else
|
||||
removeIndentation(cursor);
|
||||
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
|
||||
}
|
||||
cursor.endEditBlock();
|
||||
|
||||
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, ' '));
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
void CodeEditor::removeIndentation(QTextCursor &cursor)
|
||||
{
|
||||
// remove tab if there is a tab
|
||||
cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
||||
const QString text = cursor.selectedText();
|
||||
|
|
@ -252,20 +241,17 @@ bool CodeEditor::indentSelection(bool indent)
|
|||
for (int i = 0; i < index; ++i)
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
}
|
||||
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
|
||||
}
|
||||
cursor.endEditBlock();
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
setTextCursor(cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CodeEditor::setFont(const QFont &f)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class CodeEditor : public QPlainTextEdit
|
|||
public:
|
||||
explicit CodeEditor(QWidget *parent = nullptr);
|
||||
|
||||
void gutterAreaPaintEvent(QPaintEvent *event);
|
||||
int gutterAreaWidth();
|
||||
|
||||
void addErrorMarker(int position, int length);
|
||||
|
|
@ -47,9 +46,16 @@ private:
|
|||
int m_tabSize = 0; // tabSize in characters
|
||||
bool m_useTab = false;
|
||||
|
||||
bool lineHasError(int blockNumber) const;
|
||||
void updateExtraSelections();
|
||||
bool indentSelection(bool indent);
|
||||
void drawGutterErrorMarker(QPainter &painter, int top);
|
||||
|
||||
void insertIndentation(QTextCursor &cursor);
|
||||
void removeIndentation(QTextCursor &cursor);
|
||||
void makeSelection(QTextCursor &cursor, int first_pos, int end_block);
|
||||
|
||||
friend class GutterPainter;
|
||||
};
|
||||
|
||||
#endif // CODEEDITOR_H
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "EditorGutter.h"
|
||||
#include "CodeEditor.h"
|
||||
#include "GutterPainter.h"
|
||||
|
||||
EditorGutter::EditorGutter(CodeEditor *editor)
|
||||
: QWidget(editor)
|
||||
|
|
@ -13,5 +14,6 @@ QSize EditorGutter::sizeHint() const
|
|||
|
||||
void EditorGutter::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
codeEditor->gutterAreaPaintEvent(event);
|
||||
GutterPainter gutterpainter(codeEditor, event);
|
||||
gutterpainter.Paint();
|
||||
}
|
||||
|
|
|
|||
69
pglab/codeeditor/GutterPainter.cpp
Normal file
69
pglab/codeeditor/GutterPainter.cpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#include "GutterPainter.h"
|
||||
#include "CodeEditor.h"
|
||||
|
||||
|
||||
GutterPainter::GutterPainter(CodeEditor *editor, QPaintEvent *event)
|
||||
: editor(editor)
|
||||
, painter(editor->gutterArea)
|
||||
, event(event)
|
||||
, fontMetrics(editor->fontMetrics())
|
||||
{}
|
||||
|
||||
void GutterPainter::Paint()
|
||||
{
|
||||
painter.fillRect(event->rect(), Qt::lightGray);
|
||||
|
||||
LoopState loopState(editor);
|
||||
// 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 (loopState.block.isValid() && loopState.top <= event->rect().bottom())
|
||||
{
|
||||
if (loopState.block.isVisible() && loopState.bottom >= event->rect().top())
|
||||
{
|
||||
drawLineNumber(loopState);
|
||||
if (editor->lineHasError(loopState.blockNumber()))
|
||||
drawGutterErrorMarker(loopState);
|
||||
}
|
||||
|
||||
loopState.advanceToNextLine();
|
||||
}
|
||||
}
|
||||
|
||||
void GutterPainter::drawLineNumber(const LoopState &loopState)
|
||||
{
|
||||
QString number = QString::number(loopState.blockNumber() + 1);
|
||||
painter.setPen(Qt::black);
|
||||
painter.drawText(0, loopState.top, editor->gutterArea->width(), fontMetrics.height(),
|
||||
Qt::AlignRight, number);
|
||||
}
|
||||
|
||||
void GutterPainter::drawGutterErrorMarker(const LoopState &loopState)
|
||||
{
|
||||
int s = fontMetrics.height() - 8;
|
||||
painter.setBrush(QBrush(Qt::red));
|
||||
painter.drawEllipse(4, loopState.top + 4, s, s);
|
||||
}
|
||||
|
||||
GutterPainter::LoopState::LoopState(CodeEditor *editor)
|
||||
: editor(editor)
|
||||
, block(editor->firstVisibleBlock())
|
||||
, top((int) editor->blockBoundingGeometry(block).translated(editor->contentOffset()).top())
|
||||
, bottom(top + (int) editor->blockBoundingRect(block).height())
|
||||
{}
|
||||
|
||||
int GutterPainter::LoopState::blockNumber() const
|
||||
{
|
||||
return block.blockNumber();
|
||||
}
|
||||
|
||||
void GutterPainter::LoopState::advanceToNextLine()
|
||||
{
|
||||
block = block.next();
|
||||
top = bottom;
|
||||
bottom = top + (int) editor->blockBoundingRect(block).height();
|
||||
}
|
||||
38
pglab/codeeditor/GutterPainter.h
Normal file
38
pglab/codeeditor/GutterPainter.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include <QPainter>
|
||||
#include <QTextBlock>
|
||||
|
||||
#pragma once
|
||||
|
||||
class CodeEditor;
|
||||
class QPaintEvent;
|
||||
|
||||
class GutterPainter
|
||||
{
|
||||
public:
|
||||
GutterPainter(CodeEditor *editor, QPaintEvent *event);
|
||||
|
||||
void Paint();
|
||||
private:
|
||||
CodeEditor *editor;
|
||||
QPainter painter;
|
||||
QPaintEvent *event;
|
||||
QFontMetrics fontMetrics;
|
||||
|
||||
// Loop state
|
||||
struct LoopState {
|
||||
CodeEditor *editor;
|
||||
QTextBlock block;
|
||||
int top;
|
||||
int bottom;
|
||||
|
||||
LoopState(CodeEditor *editor);
|
||||
|
||||
int blockNumber() const;
|
||||
|
||||
void advanceToNextLine();
|
||||
};
|
||||
|
||||
void drawLineNumber(const LoopState &loopState);
|
||||
|
||||
void drawGutterErrorMarker(const LoopState &loopState);
|
||||
};
|
||||
|
|
@ -70,7 +70,7 @@ void CrudTab::on_actionRemove_rows_triggered()
|
|||
{
|
||||
std::set<IntegerRange<int>> row_ranges;
|
||||
auto selection = m_SortFilterProxy->mapSelectionToSource(ui->tableView->selectionModel()->selection());
|
||||
for (auto range : selection) {
|
||||
for (auto& range : selection) {
|
||||
row_ranges.emplace(range.top(), range.height());
|
||||
}
|
||||
std::set<IntegerRange<int>> merged_ranges;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ SOURCES += main.cpp\
|
|||
catalog/widgets/ColumnPage.cpp \
|
||||
catalog/widgets/DependantsPage.cpp \
|
||||
catalog/widgets/TriggerPage.cpp \
|
||||
codeeditor/GutterPainter.cpp \
|
||||
crud/CrudModel.cpp \
|
||||
crud/CrudTab.cpp \
|
||||
querytool/QueryExplainModel.cpp \
|
||||
|
|
@ -119,6 +120,7 @@ HEADERS += \
|
|||
catalog/widgets/ColumnPage.h \
|
||||
catalog/widgets/DependantsPage.h \
|
||||
catalog/widgets/TriggerPage.h \
|
||||
codeeditor/GutterPainter.h \
|
||||
crud/CrudModel.h \
|
||||
crud/CrudTab.h \
|
||||
querytool/QueryExplainModel.h \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue