Revision control

++ // Copyright (C) 2024 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ #include "codeeditor.h"
++ #include "linenumberarea.h"
++
++ #include <QPainter>
++ #include <QTextBlock>
++
++ CodeEditor::CodeEditor(QWidget *parent)
++ : QPlainTextEdit{parent}
++ , m_lineNumberArea{new LineNumberArea{this}}
++ {
++ setupConnections();
++ updateLineNumberAreaWidth();
++ highlightCurrentLine();
++ }
++
++ void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
++ {
++ const QRect eventRect = event->rect();
++
++ QPainter painter{m_lineNumberArea};
++ painter.fillRect(eventRect, palette().color(QPalette::AlternateBase));
++
++ QTextBlock block = firstVisibleBlock();
++ int blockNumber = block.blockNumber();
++ qreal top = blockBoundingGeometry(block).translated(contentOffset()).top();
++ qreal bottom = top + blockBoundingRect(block).height();
++
++ while (block.isVisible() && (top <= eventRect.bottom()) && (blockNumber < blockCount())) {
++ if (block.isVisible() && (bottom >= eventRect.top())) {
++ const QString number = QString::number(blockNumber + 1);
++ painter.setPen(palette().color(QPalette::Text));
++ painter.drawText(0, qRound(top), m_lineNumberArea->width() - 5, fontMetrics().height(),
++ Qt::AlignRight, number);
++ }
++
++ block = block.next();
++ top = bottom;
++ bottom = top + blockBoundingRect(block).height();
++ ++blockNumber;
++ }
++ }
++
++ int CodeEditor::lineNumberAreaWidth() const
++ {
++ int digits = 1;
++ int max = qMax(1, blockCount());
++ while (max >= 10) {
++ max /= 10;
++ ++digits;
++ }
++
++ const int space = 9 + fontMetrics().horizontalAdvance('9') * digits;
++ return space;
++ }
++
++ void CodeEditor::setupConnections()
++ {
++ connect(this, &CodeEditor::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth);
++ connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateLineNumberArea);
++ connect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::highlightCurrentLine);
++ }
++
++ void CodeEditor::resizeEvent(QResizeEvent *event)
++ {
++ QPlainTextEdit::resizeEvent(event);
++
++ const QRect rect = contentsRect();
++ m_lineNumberArea->setGeometry(rect.left(), rect.top(), lineNumberAreaWidth(), rect.height());
++ }
++
++ void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
++ {
++ if (dy)
++ m_lineNumberArea->scroll(0, dy);
++ else
++ m_lineNumberArea->update(0, rect.y(), m_lineNumberArea->width(), rect.height());
++
++ if (rect.contains(viewport()->rect()))
++ updateLineNumberAreaWidth();
++ }
++
++ void CodeEditor::updateLineNumberAreaWidth()
++ {
++ setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
++ }
++
++ void CodeEditor::highlightCurrentLine()
++ {
++ QList<QTextEdit::ExtraSelection> extraSelections;
++
++ if (!isReadOnly()) {
++ QTextEdit::ExtraSelection selection;
++ selection.format.setBackground(palette().color(QPalette::AlternateBase));
++ selection.format.setProperty(QTextFormat::FullWidthSelection, true);
++ selection.cursor = textCursor();
++ selection.cursor.clearSelection();
++
++ extraSelections.append(selection);
++ }
++
++ setExtraSelections(extraSelections);
++ }