| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- #include "vtextedit.h"
- #include <QDebug>
- #include <QScrollBar>
- #include <QPainter>
- #include <QResizeEvent>
- #include "vimageresourcemanager2.h"
- #define VIRTUAL_CURSOR_BLOCK_WIDTH 8
- enum class BlockState
- {
- Normal = 0,
- CodeBlockStart,
- CodeBlock,
- CodeBlockEnd,
- Comment
- };
- VTextEdit::VTextEdit(QWidget *p_parent)
- : QTextEdit(p_parent),
- m_imageMgr(nullptr)
- {
- init();
- }
- VTextEdit::VTextEdit(const QString &p_text, QWidget *p_parent)
- : QTextEdit(p_text, p_parent),
- m_imageMgr(nullptr)
- {
- init();
- }
- VTextEdit::~VTextEdit()
- {
- delete m_imageMgr;
- }
- void VTextEdit::init()
- {
- setAcceptRichText(false);
- m_defaultCursorWidth = 1;
- m_lineNumberType = LineNumberType::None;
- m_blockImageEnabled = false;
- m_cursorBlockMode = CursorBlock::None;
- m_highlightCursorLineBlock = false;
- m_enableExtraBuffer = false;
- m_imageMgr = new VImageResourceManager2();
- QTextDocument *doc = document();
- VTextDocumentLayout *docLayout = new VTextDocumentLayout(doc, m_imageMgr);
- docLayout->setBlockImageEnabled(m_blockImageEnabled);
- doc->setDocumentLayout(docLayout);
- docLayout->setVirtualCursorBlockWidth(VIRTUAL_CURSOR_BLOCK_WIDTH);
- docLayout->setCursorWidth(m_defaultCursorWidth);
- connect(docLayout, &VTextDocumentLayout::cursorBlockWidthUpdated,
- this, [this](int p_width) {
- if (p_width != cursorWidth()
- && p_width > VIRTUAL_CURSOR_BLOCK_WIDTH) {
- setCursorWidth(p_width);
- }
- });
- m_lineNumberArea = new VLineNumberArea(this,
- document(),
- fontMetrics().width(QLatin1Char('8')),
- this);
- connect(doc, &QTextDocument::blockCountChanged,
- this, &VTextEdit::updateLineNumberAreaMargin);
- connect(this, &QTextEdit::textChanged,
- this, &VTextEdit::updateLineNumberArea);
- connect(verticalScrollBar(), &QScrollBar::valueChanged,
- this, &VTextEdit::updateLineNumberArea);
- connect(this, &QTextEdit::cursorPositionChanged,
- this, [this]() {
- if (m_highlightCursorLineBlock) {
- QTextCursor cursor = textCursor();
- getLayout()->setCursorLineBlockNumber(cursor.block().blockNumber());
- }
- updateLineNumberArea();
- });
- }
- VTextDocumentLayout *VTextEdit::getLayout() const
- {
- return qobject_cast<VTextDocumentLayout *>(document()->documentLayout());
- }
- void VTextEdit::setLineLeading(qreal p_leading)
- {
- getLayout()->setLineLeading(p_leading);
- }
- void VTextEdit::resizeEvent(QResizeEvent *p_event)
- {
- QTextEdit::resizeEvent(p_event);
- QRect rect = contentsRect();
- if (m_lineNumberType != LineNumberType::None) {
- m_lineNumberArea->setGeometry(QRect(rect.left(),
- rect.top(),
- m_lineNumberArea->calculateWidth(),
- rect.height()));
- }
- if (m_enableExtraBuffer) {
- getLayout()->setExtraBufferHeight(rect.height() / 2);
- }
- }
- void VTextEdit::paintLineNumberArea(QPaintEvent *p_event)
- {
- if (m_lineNumberType == LineNumberType::None) {
- updateLineNumberAreaMargin();
- m_lineNumberArea->hide();
- return;
- }
- QPainter painter(m_lineNumberArea);
- painter.fillRect(p_event->rect(), m_lineNumberArea->getBackgroundColor());
- QTextBlock block = firstVisibleBlock();
- if (!block.isValid()) {
- return;
- }
- VTextDocumentLayout *layout = getLayout();
- Q_ASSERT(layout);
- int blockNumber = block.blockNumber();
- QRectF rect = layout->blockBoundingRect(block);
- int top = contentOffsetY() + (int)rect.y();
- int bottom = top + (int)rect.height();
- int eventTop = p_event->rect().top();
- int eventBtm = p_event->rect().bottom();
- const int digitHeight = painter.fontMetrics().height();
- const int curBlockNumber = textCursor().block().blockNumber();
- painter.setPen(m_lineNumberArea->getForegroundColor());
- const int leading = (int)layout->getLineLeading();
- // Display line number only in code block.
- if (m_lineNumberType == LineNumberType::CodeBlock) {
- int number = 0;
- while (block.isValid() && top <= eventBtm) {
- int blockState = block.userState();
- switch (blockState) {
- case (int)BlockState::CodeBlockStart:
- Q_ASSERT(number == 0);
- number = 1;
- break;
- case (int)BlockState::CodeBlockEnd:
- number = 0;
- break;
- case (int)BlockState::CodeBlock:
- if (number == 0) {
- // Need to find current line number in code block.
- QTextBlock startBlock = block.previous();
- while (startBlock.isValid()) {
- if (startBlock.userState() == (int)BlockState::CodeBlockStart) {
- number = block.blockNumber() - startBlock.blockNumber();
- break;
- }
- startBlock = startBlock.previous();
- }
- }
- break;
- default:
- break;
- }
- if (blockState == (int)BlockState::CodeBlock) {
- if (block.isVisible() && bottom >= eventTop) {
- QString numberStr = QString::number(number);
- painter.drawText(0,
- top + leading,
- m_lineNumberArea->width(),
- digitHeight,
- Qt::AlignRight | Qt::AlignTop,
- numberStr);
- }
- ++number;
- }
- block = block.next();
- top = bottom;
- bottom = top + (int)layout->blockBoundingRect(block).height();
- }
- return;
- }
- // Handle m_lineNumberType 1 and 2.
- Q_ASSERT(m_lineNumberType == LineNumberType::Absolute
- || m_lineNumberType == LineNumberType::Relative);
- while (block.isValid() && top <= eventBtm) {
- if (block.isVisible() && bottom >= eventTop) {
- bool currentLine = false;
- int number = blockNumber + 1;
- if (m_lineNumberType == LineNumberType::Relative) {
- number = blockNumber - curBlockNumber;
- if (number == 0) {
- currentLine = true;
- number = blockNumber + 1;
- } else if (number < 0) {
- number = -number;
- }
- } else if (blockNumber == curBlockNumber) {
- currentLine = true;
- }
- QString numberStr = QString::number(number);
- if (currentLine) {
- QFont font = painter.font();
- font.setBold(true);
- painter.setFont(font);
- }
- painter.drawText(0,
- top + leading,
- m_lineNumberArea->width(),
- digitHeight,
- Qt::AlignRight | Qt::AlignTop,
- numberStr);
- if (currentLine) {
- QFont font = painter.font();
- font.setBold(false);
- painter.setFont(font);
- }
- }
- block = block.next();
- top = bottom;
- bottom = top + (int)layout->blockBoundingRect(block).height();
- ++blockNumber;
- }
- }
- void VTextEdit::updateLineNumberAreaMargin()
- {
- int width = 0;
- if (m_lineNumberType != LineNumberType::None) {
- width = m_lineNumberArea->calculateWidth();
- }
- if (width != viewportMargins().left()) {
- setViewportMargins(width, 0, 0, 0);
- }
- }
- void VTextEdit::updateLineNumberArea()
- {
- if (m_lineNumberType != LineNumberType::None) {
- if (!m_lineNumberArea->isVisible()) {
- updateLineNumberAreaMargin();
- m_lineNumberArea->show();
- }
- m_lineNumberArea->update();
- } else if (m_lineNumberArea->isVisible()) {
- updateLineNumberAreaMargin();
- m_lineNumberArea->hide();
- }
- }
- int VTextEdit::firstVisibleBlockNumber() const
- {
- VTextDocumentLayout *layout = getLayout();
- Q_ASSERT(layout);
- return layout->findBlockByPosition(QPointF(0, -contentOffsetY()));
- }
- QTextBlock VTextEdit::firstVisibleBlock() const
- {
- VTextDocumentLayout *layout = getLayout();
- Q_ASSERT(layout);
- int blockNumber = layout->findBlockByPosition(QPointF(0, -contentOffsetY()));
- return document()->findBlockByNumber(blockNumber);
- }
- QTextBlock VTextEdit::lastVisibleBlock() const
- {
- VTextDocumentLayout *layout = getLayout();
- Q_ASSERT(layout);
- int blockNumber = layout->findBlockByPosition(QPointF(0, -contentOffsetY() + contentsRect().height()));
- return document()->findBlockByNumber(blockNumber);
- }
- void VTextEdit::visibleBlockRange(int &p_first, int &p_last) const
- {
- VTextDocumentLayout *layout = getLayout();
- Q_ASSERT(layout);
- p_first = layout->findBlockByPosition(QPointF(0, -contentOffsetY()));
- p_last = layout->findBlockByPosition(QPointF(0, -contentOffsetY() + contentsRect().height()));
- }
- int VTextEdit::contentOffsetY() const
- {
- QScrollBar *sb = verticalScrollBar();
- return -(sb->value());
- }
- void VTextEdit::clearBlockImages()
- {
- m_imageMgr->clear();
- getLayout()->relayout();
- }
- void VTextEdit::relayout(const OrderedIntSet &p_blocks)
- {
- if (p_blocks.isEmpty()) {
- return;
- }
- getLayout()->relayout(p_blocks);
- updateLineNumberArea();
- }
- void VTextEdit::relayoutVisibleBlocks()
- {
- int first, last;
- visibleBlockRange(first, last);
- OrderedIntSet blocks;
- for (int i = first; i <= last; ++i) {
- blocks.insert(i, QMapDummyValue());
- }
- getLayout()->relayout(blocks);
- updateLineNumberArea();
- }
- bool VTextEdit::containsImage(const QString &p_imageName) const
- {
- return m_imageMgr->contains(p_imageName);
- }
- QSize VTextEdit::imageSize(const QString &p_imageName) const
- {
- const QPixmap *img = m_imageMgr->findImage(p_imageName);
- if (img) {
- return img->size();
- }
- return QSize();
- }
- const QPixmap *VTextEdit::findImage(const QString &p_name) const
- {
- return m_imageMgr->findImage(p_name);
- }
- void VTextEdit::addImage(const QString &p_imageName, const QPixmap &p_image)
- {
- if (m_blockImageEnabled) {
- m_imageMgr->addImage(p_imageName, p_image);
- }
- }
- void VTextEdit::removeImage(const QString &p_imageName)
- {
- m_imageMgr->removeImage(p_imageName);
- }
- void VTextEdit::setBlockImageEnabled(bool p_enabled)
- {
- if (m_blockImageEnabled == p_enabled) {
- return;
- }
- m_blockImageEnabled = p_enabled;
- getLayout()->setBlockImageEnabled(m_blockImageEnabled);
- if (!m_blockImageEnabled) {
- clearBlockImages();
- }
- }
- void VTextEdit::setImageWidthConstrainted(bool p_enabled)
- {
- getLayout()->setImageWidthConstrainted(p_enabled);
- }
- void VTextEdit::setImageLineColor(const QColor &p_color)
- {
- getLayout()->setImageLineColor(p_color);
- }
- void VTextEdit::setCursorBlockMode(CursorBlock p_mode)
- {
- VTextDocumentLayout *layout = getLayout();
- if (p_mode != m_cursorBlockMode) {
- m_cursorBlockMode = p_mode;
- layout->setCursorBlockMode(m_cursorBlockMode);
- layout->clearLastCursorBlockWidth();
- setCursorWidth(m_cursorBlockMode != CursorBlock::None ? VIRTUAL_CURSOR_BLOCK_WIDTH
- : m_defaultCursorWidth);
- layout->updateBlockByNumber(textCursor().blockNumber());
- }
- }
- void VTextEdit::setHighlightCursorLineBlockEnabled(bool p_enabled)
- {
- if (m_highlightCursorLineBlock != p_enabled) {
- auto layout = getLayout();
- m_highlightCursorLineBlock = p_enabled;
- layout->setHighlightCursorLineBlockEnabled(p_enabled);
- if (m_highlightCursorLineBlock) {
- QTextCursor cursor = textCursor();
- layout->setCursorLineBlockNumber(cursor.block().blockNumber());
- }
- }
- }
- void VTextEdit::setCursorLineBlockBg(const QColor &p_bg)
- {
- getLayout()->setCursorLineBlockBg(p_bg);
- }
- void VTextEdit::relayout()
- {
- getLayout()->relayout();
- updateLineNumberArea();
- }
- void VTextEdit::setDisplayScaleFactor(qreal p_factor)
- {
- m_defaultCursorWidth = p_factor + 0.5;
- setCursorWidth(m_cursorBlockMode != CursorBlock::None ? VIRTUAL_CURSOR_BLOCK_WIDTH
- : m_defaultCursorWidth);
- getLayout()->setCursorWidth(m_defaultCursorWidth);
- }
- void VTextEdit::updateLineNumberAreaWidth(const QFontMetrics &p_metrics)
- {
- m_lineNumberArea->setDigitWidth(p_metrics.width(QLatin1Char('8')));
- updateLineNumberAreaMargin();
- }
- void VTextEdit::dragMoveEvent(QDragMoveEvent *p_event)
- {
- QTextEdit::dragMoveEvent(p_event);
- // We need to update the cursor rect to show the cursor while dragging text.
- // This is a work-around. We do not know why VTextEdit won't update the cursor
- // rect to show the cursor.
- // TODO: find out the rect of current cursor to update that rect only.
- update();
- }
- int VTextEdit::lineNumberAreaWidth() const
- {
- return m_lineNumberArea->width();
- }
|