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 &currentLine;
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"