// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "codeeditor.h" #include "linenumberarea.h" #include #include 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 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); }