File indexing completed on 2024-05-12 05:52:05
0001 /* 0002 SPDX-FileCopyrightText: 2022 Waqar Ahmed <waqar.17a@gmail.com> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 #include "difflinenumarea.h" 0006 #include "diffeditor.h" 0007 0008 #include <QPainter> 0009 0010 #include <KTextEditor/Editor> 0011 0012 static constexpr int Margin = 4; 0013 0014 struct LineNumColors { 0015 const QPen &otherLine; 0016 // const QPen ¤tLine; 0017 const QPen &added; 0018 const QPen &removed; 0019 }; 0020 0021 LineNumArea::LineNumArea(DiffEditor *parent) 0022 : QWidget(parent) 0023 , textEdit(parent) 0024 { 0025 setFont(textEdit->font()); 0026 auto updateColors = [this](KTextEditor::Editor *e) { 0027 if (!e) 0028 return; 0029 auto theme = e->theme(); 0030 // m_currentLineBgColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::CurrentLine)); 0031 // m_currentLineColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::CurrentLineNumber)); 0032 m_otherLinesColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::LineNumbers)); 0033 m_borderColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::Separator)); 0034 auto bg = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::IconBorder)); 0035 auto pal = palette(); 0036 pal.setColor(QPalette::Window, bg); 0037 setPalette(pal); 0038 }; 0039 connect(KTextEditor::Editor::instance(), &KTextEditor::Editor::configChanged, this, updateColors); 0040 updateColors(KTextEditor::Editor::instance()); 0041 } 0042 0043 int LineNumArea::lineNumAreaWidth() const 0044 { 0045 int digits = 1; 0046 int max = std::max(1, maxLineNum); 0047 while (max >= 10) { 0048 max /= 10; 0049 ++digits; 0050 } 0051 return 13 + textEdit->fontMetrics().horizontalAdvance(u'9') * digits; 0052 } 0053 0054 void LineNumArea::setLineNumData(std::vector<int> leftLineNos, std::vector<int> rightLineNos) 0055 { 0056 m_lineToNumA = std::move(leftLineNos); 0057 m_lineToNumB = std::move(rightLineNos); 0058 } 0059 0060 QSize LineNumArea::sizeHint() const 0061 { 0062 int lineNosWidth = lineNumAreaWidth(); 0063 if (!m_lineToNumB.empty()) { 0064 lineNosWidth *= 2; 0065 } 0066 return {lineNosWidth + textEdit->fontMetrics().height(), 0}; 0067 } 0068 0069 static void paintTriangle(QPainter &painter, QColor c, int xOffset, int yOffset, int width, int height, bool open) 0070 { 0071 painter.setRenderHint(QPainter::Antialiasing); 0072 0073 qreal size = qMin(width, height); 0074 0075 QPen pen; 0076 pen.setJoinStyle(Qt::RoundJoin); 0077 pen.setColor(c); 0078 pen.setWidthF(1.5); 0079 painter.setPen(pen); 0080 painter.setBrush(c); 0081 0082 // let some border, if possible 0083 size *= 0.6; 0084 0085 qreal halfSize = size / 2; 0086 qreal halfSizeP = halfSize * 0.6; 0087 QPointF middle(xOffset + (qreal)width / 2, yOffset + (qreal)height / 2); 0088 0089 if (open) { 0090 QPointF points[3] = {middle + QPointF(-halfSize, -halfSizeP), middle + QPointF(halfSize, -halfSizeP), middle + QPointF(0, halfSizeP)}; 0091 painter.drawConvexPolygon(points, 3); 0092 } else { 0093 QPointF points[3] = {middle + QPointF(-halfSizeP, -halfSize), middle + QPointF(-halfSizeP, halfSize), middle + QPointF(halfSizeP, 0)}; 0094 painter.drawConvexPolygon(points, 3); 0095 } 0096 0097 painter.setRenderHint(QPainter::Antialiasing, false); 0098 } 0099 0100 void LineNumArea::paintEvent(QPaintEvent *event) 0101 { 0102 if (m_lineToNumA.empty()) { 0103 return; 0104 } 0105 QPainter painter(this); 0106 0107 painter.fillRect(event->rect(), palette().color(QPalette::Active, QPalette::Window)); 0108 0109 auto block = textEdit->firstVisibleBlock(); 0110 int blockNumber = block.blockNumber(); 0111 qreal top = textEdit->blockBoundingGeometry(block).translated(textEdit->contentOffset()).top(); 0112 // Maybe the top is not 0? 0113 top += textEdit->viewportMargins().top(); 0114 qreal bottom = top; 0115 0116 // const QPen currentLine = m_currentLineColor; 0117 const QPen otherLines = m_otherLinesColor; 0118 0119 QColor c = textEdit->addedColor(); 0120 c.setAlphaF(0.6f); 0121 const QPen added = c; 0122 c = textEdit->removedColor(); 0123 c.setAlphaF(0.6f); 0124 const QPen removed = c; 0125 0126 LineNumColors colors{otherLines /*, currentLine*/, added, removed}; 0127 0128 painter.setFont(font()); 0129 const int w = lineNumAreaWidth(); 0130 0131 while (block.isValid() && top <= event->rect().bottom()) { 0132 top = bottom; 0133 bottom = top + textEdit->blockBoundingRect(block).height(); 0134 if (block.isVisible() && bottom >= event->rect().top()) { 0135 // Current line background 0136 // const auto isCurrentLine = textEdit->textCursor().blockNumber() == blockNumber; 0137 // if (isCurrentLine) { 0138 // painter.fillRect(0, top, rect().width(), textEdit->fontMetrics().height(), m_currentLineBgColor); 0139 // } 0140 0141 // Line number left 0142 if (size_t(blockNumber) < m_lineToNumA.size()) { 0143 int n = m_lineToNumA[blockNumber]; 0144 QRect numRect(0, top, w, textEdit->fontMetrics().height()); 0145 drawLineNumber(painter, numRect, blockNumber, n, colors); 0146 } 0147 0148 if (!m_lineToNumB.empty()) { 0149 // we are in unified mode, draw the right line number 0150 if (size_t(blockNumber) < m_lineToNumB.size()) { 0151 int n = m_lineToNumB[blockNumber]; 0152 QRect numRect(w, top, w, textEdit->fontMetrics().height()); 0153 drawLineNumber(painter, numRect, blockNumber, n, colors); 0154 } 0155 } 0156 0157 // Hunk fold marker 0158 if (textEdit->isHunkLine(blockNumber)) { 0159 const int x = rect().width() - (textEdit->fontMetrics().height() + Margin); 0160 const int y = top; 0161 const int width = textEdit->fontMetrics().height(); 0162 const int height = width; 0163 paintTriangle(painter, m_otherLinesColor, x, y, width, height, !textEdit->isHunkFolded(blockNumber)); 0164 } 0165 } 0166 0167 block = block.next(); 0168 ++blockNumber; 0169 } 0170 0171 // draw the line num area border 0172 if (m_lineToNumB.empty()) { 0173 // side by side 0174 painter.setPen(m_borderColor); 0175 painter.drawLine(rect().topRight() - QPoint(1, 0), rect().bottomRight() - QPoint(1, 0)); 0176 } else { 0177 // unified 0178 painter.setPen(m_borderColor); 0179 painter.drawLine(QPoint(w, 0) - QPoint(1, 0), QPoint(w, rect().bottom()) - QPoint(1, 0)); 0180 painter.setPen(m_borderColor); 0181 painter.drawLine(rect().topRight() - QPoint(1, 0), rect().bottomRight() - QPoint(1, 0)); 0182 } 0183 } 0184 0185 void LineNumArea::drawLineNumber(QPainter &painter, QRect rect, int blockNumber, int num, const LineNumColors &c) 0186 { 0187 if (num < 0) { 0188 return; 0189 } 0190 0191 const QString number = QString::number(num); 0192 QPen p = c.otherLine; 0193 auto hl = textEdit->highlightingForLine(blockNumber); 0194 if (hl) { 0195 p = hl->added ? c.added : c.removed; 0196 } 0197 0198 painter.setPen(p); 0199 rect.adjust(0, 0, -(Margin * 2), 0); 0200 painter.drawText(rect, Qt::AlignRight | Qt::AlignVCenter | Qt::TextDontClip, number); 0201 } 0202 0203 void LineNumArea::mousePressEvent(QMouseEvent *e) 0204 { 0205 auto pos = e->pos(); 0206 auto r = rect(); 0207 r.setLeft(lineNumAreaWidth()); 0208 if (!r.contains(pos)) { 0209 QWidget::mousePressEvent(e); 0210 return; 0211 } 0212 0213 int block = textEdit->cursorForPosition(pos).block().blockNumber(); 0214 if (textEdit->isHunkLine(block)) { 0215 textEdit->toggleFoldHunk(block); 0216 } 0217 } 0218 0219 #include "moc_difflinenumarea.cpp"