1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#include "linenumbertexteditor.h"
18
19#include <QPainter>
20#include <QTextBlock>
21#include <QDebug>
22
23LineNumberTextEditor::LineNumberTextEditor(QWidget *parent):QPlainTextEdit(parent)
24{
25 lineNumberArea = new LineNumberArea(this);
26
27 connect(this, &LineNumberTextEditor::blockCountChanged, this, &LineNumberTextEditor::updateLineNumberAreaWidth);
28 connect(this, &LineNumberTextEditor::updateRequest, this, &LineNumberTextEditor::updateLineNumberArea);
29 connect(this, &LineNumberTextEditor::cursorPositionChanged, this, &LineNumberTextEditor::highlightCurrentLine);
30 updateLineNumberAreaWidth(0);
31 //highlightCurrentLine();
32}
33
34int LineNumberTextEditor::lineNumberAreaWidth()
35{
36 int digits = 1;
37 int max = qMax(1, blockCount());
38 while (max >= 10) {
39 max /= 10;
40 ++digits;
41 }
42
43 int space = 10 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
44
45 return space;
46}
47
48void LineNumberTextEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
49{
50 setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
51}
52
53void LineNumberTextEditor::updateLineNumberArea(const QRect &rect, int dy)
54{
55 if (dy)
56 lineNumberArea->scroll(0, dy);
57 else
58 lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
59
60 if (rect.contains(viewport()->rect()))
61 updateLineNumberAreaWidth(0);
62}
63
64const QColor &LineNumberTextEditor::lineNumberAreaCurrentLine() const
65{
66 return mLineNumberAreaCurrentLine;
67}
68
69void LineNumberTextEditor::setLineNumberAreaCurrentLine(const QColor &newLineNumberAreaCurrentLine)
70{
71 if (mLineNumberAreaCurrentLine == newLineNumberAreaCurrentLine)
72 return;
73 mLineNumberAreaCurrentLine = newLineNumberAreaCurrentLine;
74 emit lineNumberAreaCurrentLineChanged();
75}
76
77const QColor &LineNumberTextEditor::lineNumberAreaBackground() const
78{
79 return mLineNumberAreaBackground;
80}
81
82void LineNumberTextEditor::setLineNumberAreaBackground(const QColor &newLineNumberAreaBackground)
83{
84 mLineNumberAreaBackground = newLineNumberAreaBackground;
85}
86
87const QColor &LineNumberTextEditor::lineNumberAreaForeground() const
88{
89 return mLineNumberAreaForeground;
90}
91
92void LineNumberTextEditor::setLineNumberAreaForeground(const QColor &newLineNumberAreaForeground)
93{
94 mLineNumberAreaForeground = newLineNumberAreaForeground;
95}
96
97void LineNumberTextEditor::resizeEvent(QResizeEvent *e)
98{
99 QPlainTextEdit::resizeEvent(e);
100
101 QRect cr = contentsRect();
102 lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
103}
104
105void LineNumberTextEditor::highlightCurrentLine()
106{
107 lineNumberArea->update();
108// QList<QTextEdit::ExtraSelection> extraSelections;
109
110// if (!isReadOnly()) {
111// QTextEdit::ExtraSelection selection;
112
113// QColor lineColor = QColor(Qt::yellow).lighter(160);
114
115// selection.format.setBackground(lineColor);
116// selection.format.setProperty(QTextFormat::FullWidthSelection, true);
117// selection.cursor = textCursor();
118// selection.cursor.clearSelection();
119// extraSelections.append(selection);
120// }
121
122// setExtraSelections(extraSelections);
123}
124
125void LineNumberTextEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
126{
127 QPainter painter(lineNumberArea);
128 painter.setFont(font());
129 if (isEnabled())
130 painter.fillRect(event->rect(), mLineNumberAreaBackground);
131 else
132 painter.fillRect(event->rect(), palette().color(QPalette::Disabled, QPalette::Button));
133 QTextBlock block = firstVisibleBlock();
134 int blockNumber = block.blockNumber();
135 int top = qRound(blockBoundingGeometry(block).translated(contentOffset()).top());
136 int bottom = top + qRound(blockBoundingRect(block).height());
137 while (block.isValid() && top <= event->rect().bottom()) {
138 if (block.isVisible() && bottom >= event->rect().top()) {
139 QString number = QString::number(blockNumber + 1);
140 if (!isEnabled())
141 painter.setPen(palette().color(QPalette::Disabled,QPalette::ButtonText));
142 else if (textCursor().blockNumber()==blockNumber)
143 painter.setPen(mLineNumberAreaCurrentLine);
144 else
145 painter.setPen(mLineNumberAreaForeground);
146 painter.drawText(5, top, lineNumberArea->width()-10, fontMetrics().height(),
147 Qt::AlignRight, number);
148 }
149
150 block = block.next();
151 top = bottom;
152 bottom = top + qRound(blockBoundingRect(block).height());
153 ++blockNumber;
154 }
155}
156