File indexing completed on 2025-01-05 05:14:52

0001 /*
0002 SPDX-FileCopyrightText: 2021 Hamed Masafi <hamed.masfi@gmail.com>
0003 
0004 SPDX-License-Identifier: GPL-3.0-or-later
0005 */
0006 
0007 #include "graphpainter.h"
0008 
0009 #include "entities/commit.h"
0010 #include "models/logsmodel.h"
0011 #include <QPainter>
0012 #include <QPainterPath>
0013 
0014 #define HEIGHT 25
0015 #define WIDTH 18
0016 
0017 QPoint center(int x)
0018 {
0019     return {(x * WIDTH) + (WIDTH / 2), HEIGHT / 2};
0020 }
0021 QPoint centerEdge(int x, Qt::Edge edge)
0022 {
0023     switch (edge) {
0024     case Qt::TopEdge:
0025         return {(x * WIDTH) + (WIDTH / 2), 0};
0026     case Qt::LeftEdge:
0027         return {(x * WIDTH), HEIGHT / 2};
0028     case Qt::RightEdge:
0029         return {(x + 1) * WIDTH, HEIGHT / 2};
0030     case Qt::BottomEdge:
0031         return {(x * WIDTH) + (WIDTH / 2), HEIGHT};
0032     }
0033     return {};
0034 }
0035 
0036 QPoint point(int col, Qt::Alignment align = Qt::AlignCenter)
0037 {
0038     int y;
0039     int x = col * WIDTH;
0040 
0041     if (align & Qt::AlignTop)
0042         y = 0;
0043     else if (align & Qt::AlignBottom)
0044         y = HEIGHT;
0045     else
0046         y = HEIGHT / 2;
0047 
0048     if (align & Qt::AlignLeft)
0049         x += 0;
0050     else if (align & Qt::AlignRight)
0051         x += WIDTH;
0052     else
0053         x += WIDTH / 2;
0054 
0055     return {x, y};
0056 }
0057 QPoint centerGuide(int x, Qt::Edge edge)
0058 {
0059     constexpr const int tel{5};
0060 
0061     QPoint pt = point(x);
0062     switch (edge) {
0063     case Qt::TopEdge:
0064         pt.setY(pt.y() - tel);
0065         break;
0066     case Qt::LeftEdge:
0067         pt.setX(pt.x() - tel);
0068         break;
0069     case Qt::RightEdge:
0070         pt.setX(pt.x() + tel);
0071         break;
0072     case Qt::BottomEdge:
0073         pt.setY(pt.y() + tel);
0074         break;
0075     }
0076     return pt;
0077 }
0078 
0079 GraphPainter::GraphPainter(Git::LogsModel *model, QObject *parent)
0080     : QStyledItemDelegate(parent)
0081     , mModel(model)
0082 {
0083     mColors = {Qt::red, Qt::blue, Qt::darkGreen, Qt::magenta, Qt::darkMagenta, Qt::darkBlue, Qt::darkBlue, Qt::darkRed, Qt::darkYellow, Qt::darkGreen};
0084 }
0085 
0086 void GraphPainter::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0087 {
0088     auto log = mModel->fromIndex(index);
0089 
0090     painter->setRenderHints(QPainter::Antialiasing);
0091 
0092     //    painter->fillRect(option.rect, option.palette.base());
0093     if (option.state & QStyle::State_Selected)
0094         painter->fillRect(option.rect, option.palette.highlight());
0095     //    else if (option.state & QStyle::State_MouseOver)
0096     //        painter->fillRect(option.rect, option.palette.brush(QPalette::Normal, QPalette::Highlight));
0097     //    else if (index.row() & 1)
0098     //        painter->fillRect(option.rect, option.palette.alternateBase());
0099     else
0100         painter->fillRect(option.rect, option.palette.base());
0101 
0102     painter->save();
0103     painter->setClipRect(option.rect, Qt::IntersectClip);
0104     painter->translate(option.rect.topLeft());
0105 
0106     int x{-1};
0107     for (auto &l : log->lanes()) {
0108         ++x;
0109         if (l.type() == Git::GraphLane::None)
0110             continue;
0111 
0112         if (x >= mColors.size()) {
0113             painter->setPen(Qt::black);
0114             painter->setBrush(Qt::black);
0115         } else {
0116             painter->setPen(mColors.at(x));
0117             painter->setBrush(mColors.at(x));
0118         }
0119         //        painter->setPen(l.color());
0120         //        painter->setBrush(l.color());
0121         paintLane(painter, l, x);
0122     }
0123 
0124     QRect rc(log->lanes().size() * WIDTH, 0, painter->fontMetrics().horizontalAdvance(log->subject()), HEIGHT);
0125 
0126     painter->setPen(option.palette.color(QPalette::Text));
0127     if (!log->reference().isNull()) {
0128         const QString refStr{QStringLiteral("ref: ")};
0129         const auto ref = refStr + log->reference()->shorthand();
0130         QRect rcBox(log->lanes().size() * WIDTH, 0, painter->fontMetrics().horizontalAdvance(ref) + 8, painter->fontMetrics().height() + 4);
0131         rcBox.moveTop((HEIGHT - rcBox.height()) / 2);
0132 
0133         QLinearGradient linearGrad(rcBox.topLeft(), rcBox.bottomRight());
0134         linearGrad.setFinalStop(rcBox.bottomLeft());
0135         linearGrad.setColorAt(0, Qt::white);
0136         linearGrad.setColorAt(1, QColor(100, 100, 255));
0137 
0138         //        painter->fillRect(rcBox.left(), rcBox.top(), painter->fontMetrics().horizontalAdvance(refStr) + 2, rcBox.height(), QBrush(linearGrad));
0139 
0140         /*painter->fillRect(rcBox.left() + painter->fontMetrics().horizontalAdvance(refStr) + 2,
0141                           rcBox.top(),
0142                           rcBox.width() - painter->fontMetrics().horizontalAdvance(refStr) - 2,
0143                           rcBox.height(),
0144                           Qt::white);*/
0145 
0146         painter->setBrush(Qt::transparent);
0147         painter->drawRoundedRect(rcBox, 5, 5);
0148         painter->drawText(rcBox, Qt::AlignVCenter | Qt::AlignHCenter, ref);
0149         rc.moveLeft(rc.left() + rcBox.width() + 6);
0150     }
0151     painter->drawText(rc, Qt::AlignVCenter, log->subject());
0152 
0153     painter->restore();
0154 }
0155 
0156 void GraphPainter::paintLane(QPainter *painter, const Git::GraphLane &lane, int index) const
0157 {
0158     switch (lane.type()) {
0159     case Git::GraphLane::Start:
0160         painter->drawLine(point(index), point(index, Qt::AlignTop));
0161         painter->setBrush(Qt::white);
0162         painter->drawEllipse(point(index), 3, 3);
0163         break;
0164     case Git::GraphLane::Pipe:
0165         painter->drawLine(point(index, Qt::AlignTop), point(index, Qt::AlignBottom));
0166         break;
0167     case Git::GraphLane::Node:
0168         painter->drawLine(point(index, Qt::AlignTop), point(index, Qt::AlignBottom));
0169         painter->setBrush(Qt::white);
0170         painter->drawEllipse(point(index), 3, 3);
0171         break;
0172     case Git::GraphLane::End:
0173         painter->drawLine(point(index), point(index, Qt::AlignBottom));
0174         painter->setBrush(Qt::white);
0175         painter->drawEllipse(point(index), 3, 3);
0176         break;
0177     case Git::GraphLane::Test:
0178         painter->drawLine(point(index, Qt::AlignTop | Qt::AlignLeft), point(index, Qt::AlignBottom | Qt::AlignRight));
0179         painter->drawLine(point(index, Qt::AlignTop | Qt::AlignRight), point(index, Qt::AlignBottom | Qt::AlignLeft));
0180         break;
0181 
0182     case Git::GraphLane::None:
0183     case Git::GraphLane::Transparent:
0184         break; // just to avoid compiler warning
0185     }
0186 
0187     for (const auto &i : lane.upJoins()) {
0188         painter->drawEllipse(point(i), 2, 2);
0189         QPainterPath p;
0190         p.moveTo(point(i));
0191         p.cubicTo(centerGuide(index, Qt::LeftEdge), centerGuide(index, Qt::TopEdge), point(index, Qt::AlignTop));
0192         painter->setBrush(Qt::transparent);
0193         painter->drawPath(p);
0194     }
0195     for (const auto &i : lane.bottomJoins()) {
0196         painter->drawEllipse(point(i), 2, 2);
0197 
0198         QPainterPath p;
0199         p.moveTo(point(index, Qt::AlignBottom));
0200         p.cubicTo(centerGuide(index, Qt::BottomEdge), centerGuide(index, Qt::LeftEdge), point(i));
0201         painter->setBrush(Qt::transparent);
0202         //        painter->setPen(Qt::DotLine);
0203         painter->drawPath(p);
0204     }
0205 }
0206 
0207 int GraphPainter::colX(int col) const
0208 {
0209     return col * WIDTH;
0210 }
0211 
0212 QSize GraphPainter::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0213 {
0214     Q_UNUSED(option)
0215     Q_UNUSED(index)
0216     return {0, HEIGHT};
0217 }
0218 
0219 // #include "moc_graphpainter.cpp"