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"