| 1 | #include "RepositoryViewDelegate.h" |
| 2 | |
| 3 | #include <Colors.h> |
| 4 | #include <CommitHistoryColumns.h> |
| 5 | #include <CommitHistoryModel.h> |
| 6 | #include <CommitHistoryView.h> |
| 7 | #include <CommitInfo.h> |
| 8 | #include <GitBase.h> |
| 9 | #include <GitCache.h> |
| 10 | #include <GitLocal.h> |
| 11 | #include <GitQlientStyles.h> |
| 12 | #include <GitServerCache.h> |
| 13 | #include <Lane.h> |
| 14 | #include <LaneType.h> |
| 15 | #include <PullRequest.h> |
| 16 | |
| 17 | #include <QApplication> |
| 18 | #include <QClipboard> |
| 19 | #include <QDesktopServices> |
| 20 | #include <QEvent> |
| 21 | #include <QPainter> |
| 22 | #include <QPainterPath> |
| 23 | #include <QSortFilterProxyModel> |
| 24 | #include <QToolTip> |
| 25 | #include <QUrl> |
| 26 | |
| 27 | using namespace GitServer; |
| 28 | |
| 29 | static const int MIN_VIEW_WIDTH_PX = 480; |
| 30 | |
| 31 | RepositoryViewDelegate::RepositoryViewDelegate(const QSharedPointer<GitCache> &cache, |
| 32 | const QSharedPointer<GitBase> &git, |
| 33 | const QSharedPointer<GitServerCache> &gitServerCache, |
| 34 | CommitHistoryView *view) |
| 35 | : mCache(cache) |
| 36 | , mGit(git) |
| 37 | , mGitServerCache(gitServerCache) |
| 38 | , mView(view) |
| 39 | { |
| 40 | } |
| 41 | |
| 42 | void RepositoryViewDelegate::paint(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const |
| 43 | { |
| 44 | p->setRenderHints(QPainter::Antialiasing); |
| 45 | |
| 46 | QStyleOptionViewItem newOpt(opt); |
| 47 | newOpt.font.setPointSize(9); |
| 48 | |
| 49 | if (newOpt.state & QStyle::State_Selected) |
| 50 | p->fillRect(newOpt.rect, GitQlientStyles::getGraphSelectionColor()); |
| 51 | else if (newOpt.state & QStyle::State_MouseOver) |
| 52 | p->fillRect(newOpt.rect, GitQlientStyles::getGraphHoverColor()); |
| 53 | |
| 54 | const auto row = mView->hasActiveFilter() |
| 55 | ? dynamic_cast<QSortFilterProxyModel *>(mView->model())->mapToSource(index).row() |
| 56 | : index.row(); |
| 57 | |
| 58 | const auto commit = mCache->commitInfo(row); |
| 59 | |
| 60 | if (commit.sha.isEmpty()) |
| 61 | return; |
| 62 | |
| 63 | if (index.column() == static_cast<int>(CommitHistoryColumns::Graph)) |
| 64 | { |
| 65 | newOpt.rect.setX(newOpt.rect.x() + 10); |
| 66 | paintGraph(p, newOpt, commit); |
| 67 | } |
| 68 | else if (index.column() == static_cast<int>(CommitHistoryColumns::Log)) |
| 69 | paintLog(p, newOpt, commit, index.data().toString()); |
| 70 | else |
| 71 | { |
| 72 | |
| 73 | p->setPen(GitQlientStyles::getTextColor()); |
| 74 | newOpt.rect.setX(newOpt.rect.x() + 10); |
| 75 | |
| 76 | QTextOption textalignment(Qt::AlignLeft | Qt::AlignVCenter); |
| 77 | auto text = index.data().toString(); |
| 78 | |
| 79 | if (index.column() == static_cast<int>(CommitHistoryColumns::Date)) |
| 80 | { |
| 81 | textalignment = QTextOption(Qt::AlignRight | Qt::AlignVCenter); |
| 82 | const auto prev = QDateTime::fromString(mView->indexAbove(index).data().toString(), "dd MMM yyyy hh:mm" ); |
| 83 | const auto current = QDateTime::fromString(text, "dd MMM yyyy hh:mm" ); |
| 84 | |
| 85 | if (current.date() == prev.date()) |
| 86 | text = current.toString("hh:mm" ); |
| 87 | else |
| 88 | text = current.toString("dd MMM yyyy - hh:mm" ); |
| 89 | |
| 90 | newOpt.rect.setWidth(newOpt.rect.width() - 5); |
| 91 | } |
| 92 | else if (index.column() == static_cast<int>(CommitHistoryColumns::Sha)) |
| 93 | { |
| 94 | newOpt.font.setPointSize(8); |
| 95 | newOpt.font.setFamily("DejaVu Sans Mono" ); |
| 96 | |
| 97 | text = commit.sha != CommitInfo::ZERO_SHA ? text.left(8) : "" ; |
| 98 | } |
| 99 | else if (index.column() == static_cast<int>(CommitHistoryColumns::Author) && commit.isSigned()) |
| 100 | { |
| 101 | static const auto size = 15; |
| 102 | static const auto offset = 5; |
| 103 | QPixmap pic(QString::fromUtf8(commit.verifiedSignature() ? ":/icons/signed" : ":/icons/unsigned" )); |
| 104 | pic = pic.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation); |
| 105 | |
| 106 | const auto inc = (newOpt.rect.height() - size) / 2; |
| 107 | |
| 108 | p->drawPixmap(QRect(newOpt.rect.x(), newOpt.rect.y() + inc, size, size), pic); |
| 109 | |
| 110 | newOpt.rect.setX(newOpt.rect.x() + size + offset); |
| 111 | } |
| 112 | |
| 113 | QFontMetrics fm(newOpt.font); |
| 114 | p->setFont(newOpt.font); |
| 115 | |
| 116 | if (const auto cursorColumn = mView->indexAt(mView->mapFromGlobal(QCursor::pos())).column(); |
| 117 | newOpt.state & QStyle::State_MouseOver && cursorColumn == index.column() |
| 118 | && cursorColumn == static_cast<int>(CommitHistoryColumns::Sha)) |
| 119 | { |
| 120 | p->setPen(gitQlientOrange); |
| 121 | } |
| 122 | |
| 123 | p->drawText(newOpt.rect, fm.elidedText(text, Qt::ElideRight, newOpt.rect.width()), textalignment); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | QSize RepositoryViewDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const |
| 128 | { |
| 129 | return QSize(LANE_WIDTH, ROW_HEIGHT); |
| 130 | } |
| 131 | |
| 132 | bool RepositoryViewDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, |
| 133 | const QModelIndex &index) |
| 134 | { |
| 135 | const auto cursorColumn = mView->indexAt(mView->mapFromGlobal(QCursor::pos())).column(); |
| 136 | |
| 137 | if (event->type() == QEvent::MouseButtonPress && cursorColumn == index.column() |
| 138 | && cursorColumn == static_cast<int>(CommitHistoryColumns::Sha)) |
| 139 | { |
| 140 | mColumnPressed = cursorColumn; |
| 141 | return true; |
| 142 | } |
| 143 | else if (event->type() == QEvent::MouseButtonRelease && cursorColumn == index.column() && mColumnPressed != -1) |
| 144 | { |
| 145 | const auto text = index.data().toString(); |
| 146 | if (cursorColumn == static_cast<int>(CommitHistoryColumns::Sha) && text != CommitInfo::ZERO_SHA) |
| 147 | { |
| 148 | QApplication::clipboard()->setText(text); |
| 149 | QToolTip::showText(QCursor::pos(), tr("Copied!" ), mView); |
| 150 | } |
| 151 | |
| 152 | mColumnPressed = -1; |
| 153 | return true; |
| 154 | } |
| 155 | |
| 156 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
| 157 | } |
| 158 | |
| 159 | void RepositoryViewDelegate::paintGraphLane(QPainter *p, const Lane &lane, bool laneHeadPresent, int x1, int x2, |
| 160 | const QColor &col, const QColor &activeCol, const QColor &mergeColor, |
| 161 | bool isWip, bool hasChilds) const |
| 162 | { |
| 163 | const auto padding = 2; |
| 164 | x1 += padding; |
| 165 | x2 += padding; |
| 166 | |
| 167 | const auto h = ROW_HEIGHT / 2; |
| 168 | const auto m = (x1 + x2) / 2; |
| 169 | const auto r = (x2 - x1) * 1 / 3; |
| 170 | const auto spanAngle = 90 * 16; |
| 171 | const auto angleWidthRight = 2 * (x1 - m); |
| 172 | const auto angleWidthLeft = 2 * (x2 - m); |
| 173 | const auto angleHeightUp = 2 * h; |
| 174 | const auto angleHeightDown = 2 * -h; |
| 175 | |
| 176 | static QPen lanePen(GitQlientStyles::getTextColor(), 2); // fast path here |
| 177 | |
| 178 | // arc |
| 179 | lanePen.setBrush(col); |
| 180 | p->setPen(lanePen); |
| 181 | |
| 182 | switch (lane.getType()) |
| 183 | { |
| 184 | case LaneType::JOIN: |
| 185 | case LaneType::JOIN_R: |
| 186 | case LaneType::HEAD: |
| 187 | case LaneType::HEAD_R: { |
| 188 | p->drawArc(m, h, angleWidthRight, angleHeightUp, 0 * 16, spanAngle); |
| 189 | break; |
| 190 | } |
| 191 | case LaneType::JOIN_L: { |
| 192 | p->drawArc(m, h, angleWidthLeft, angleHeightUp, 90 * 16, spanAngle); |
| 193 | break; |
| 194 | } |
| 195 | case LaneType::TAIL: |
| 196 | case LaneType::TAIL_R: { |
| 197 | p->drawArc(m, h, angleWidthRight, angleHeightDown, 270 * 16, spanAngle); |
| 198 | break; |
| 199 | } |
| 200 | default: |
| 201 | break; |
| 202 | } |
| 203 | |
| 204 | if (isWip) |
| 205 | { |
| 206 | lanePen.setColor(activeCol); |
| 207 | p->setPen(lanePen); |
| 208 | } |
| 209 | |
| 210 | // vertical line |
| 211 | if (!(isWip && !hasChilds)) |
| 212 | { |
| 213 | if (!isWip && !hasChilds |
| 214 | && (lane.getType() == LaneType::HEAD || lane.getType() == LaneType::INITIAL |
| 215 | || lane.getType() == LaneType::BRANCH || lane.getType() == LaneType::MERGE_FORK |
| 216 | || lane.getType() == LaneType::MERGE_FORK_R || lane.getType() == LaneType::MERGE_FORK_L |
| 217 | || lane.getType() == LaneType::ACTIVE)) |
| 218 | p->drawLine(m, h, m, 2 * h); |
| 219 | else |
| 220 | { |
| 221 | switch (lane.getType()) |
| 222 | { |
| 223 | case LaneType::ACTIVE: |
| 224 | case LaneType::NOT_ACTIVE: |
| 225 | case LaneType::MERGE_FORK: |
| 226 | case LaneType::MERGE_FORK_R: |
| 227 | case LaneType::MERGE_FORK_L: |
| 228 | case LaneType::JOIN: |
| 229 | case LaneType::JOIN_R: |
| 230 | case LaneType::JOIN_L: |
| 231 | case LaneType::CROSS: |
| 232 | p->drawLine(m, 0, m, 2 * h); |
| 233 | break; |
| 234 | case LaneType::HEAD_L: |
| 235 | case LaneType::BRANCH: |
| 236 | p->drawLine(m, h, m, 2 * h); |
| 237 | break; |
| 238 | case LaneType::TAIL_L: |
| 239 | case LaneType::INITIAL: |
| 240 | p->drawLine(m, 0, m, h); |
| 241 | break; |
| 242 | default: |
| 243 | break; |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | // center symbol |
| 249 | auto isCommit = false; |
| 250 | |
| 251 | if (isWip) |
| 252 | { |
| 253 | isCommit = true; |
| 254 | p->setPen(QPen(col, 2)); |
| 255 | p->setBrush(col); |
| 256 | p->drawEllipse(m - r + 2, h - r + 2, 8, 8); |
| 257 | } |
| 258 | else |
| 259 | { |
| 260 | switch (lane.getType()) |
| 261 | { |
| 262 | case LaneType::HEAD: |
| 263 | case LaneType::INITIAL: |
| 264 | case LaneType::BRANCH: |
| 265 | case LaneType::MERGE_FORK: |
| 266 | case LaneType::MERGE_FORK_R: |
| 267 | isCommit = true; |
| 268 | p->setPen(QPen(mergeColor, 2)); |
| 269 | p->setBrush(col); |
| 270 | p->drawEllipse(m - r + 2, h - r + 2, 8, 8); |
| 271 | break; |
| 272 | case LaneType::MERGE_FORK_L: |
| 273 | isCommit = true; |
| 274 | p->setPen(QPen(laneHeadPresent ? mergeColor : col, 2)); |
| 275 | p->setBrush(col); |
| 276 | p->drawEllipse(m - r + 2, h - r + 2, 8, 8); |
| 277 | break; |
| 278 | case LaneType::ACTIVE: { |
| 279 | isCommit = true; |
| 280 | p->setPen(QPen(col, 2)); |
| 281 | p->setBrush(QColor(isWip ? col : GitQlientStyles::getBackgroundColor())); |
| 282 | p->drawEllipse(m - r + 2, h - r + 2, 8, 8); |
| 283 | } |
| 284 | break; |
| 285 | default: |
| 286 | break; |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | lanePen.setColor(mergeColor); |
| 291 | p->setPen(lanePen); |
| 292 | |
| 293 | // horizontal line |
| 294 | switch (lane.getType()) |
| 295 | { |
| 296 | case LaneType::MERGE_FORK: |
| 297 | case LaneType::JOIN: |
| 298 | case LaneType::HEAD: |
| 299 | case LaneType::TAIL: |
| 300 | case LaneType::CROSS: |
| 301 | case LaneType::CROSS_EMPTY: |
| 302 | p->drawLine(x1 + (isCommit ? 10 : 0), h, x2, h); |
| 303 | break; |
| 304 | case LaneType::MERGE_FORK_R: |
| 305 | p->drawLine(x1 + (isCommit ? 0 : 10), h, m - (isCommit ? 6 : 0), h); |
| 306 | break; |
| 307 | case LaneType::MERGE_FORK_L: |
| 308 | case LaneType::HEAD_L: |
| 309 | case LaneType::TAIL_L: |
| 310 | p->drawLine(m + (isCommit ? 6 : 0), h, x2, h); |
| 311 | break; |
| 312 | default: |
| 313 | break; |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | QColor RepositoryViewDelegate::getMergeColor(const Lane ¤tLane, const CommitInfo &commit, int currentLaneIndex, |
| 318 | const QColor &defaultColor, bool &isSet) const |
| 319 | { |
| 320 | auto mergeColor = defaultColor; |
| 321 | //= GitQlientStyles::getBranchColorAt((commit.getLanesCount() - 1) % GitQlientStyles::getTotalBranchColors()); |
| 322 | |
| 323 | switch (currentLane.getType()) |
| 324 | { |
| 325 | case LaneType::HEAD_L: |
| 326 | case LaneType::HEAD_R: |
| 327 | case LaneType::TAIL_L: |
| 328 | case LaneType::TAIL_R: |
| 329 | case LaneType::MERGE_FORK_L: |
| 330 | case LaneType::JOIN_R: |
| 331 | isSet = true; |
| 332 | mergeColor = defaultColor; |
| 333 | break; |
| 334 | case LaneType::MERGE_FORK_R: |
| 335 | case LaneType::JOIN_L: |
| 336 | for (auto laneCount = 0; laneCount < currentLaneIndex; ++laneCount) |
| 337 | { |
| 338 | if (commit.laneAt(laneCount).equals(LaneType::JOIN_L)) |
| 339 | { |
| 340 | mergeColor = GitQlientStyles::getBranchColorAt(laneCount % GitQlientStyles::getTotalBranchColors()); |
| 341 | isSet = true; |
| 342 | break; |
| 343 | } |
| 344 | } |
| 345 | break; |
| 346 | default: |
| 347 | break; |
| 348 | } |
| 349 | |
| 350 | return mergeColor; |
| 351 | } |
| 352 | |
| 353 | void RepositoryViewDelegate::paintGraph(QPainter *p, const QStyleOptionViewItem &opt, const CommitInfo &commit) const |
| 354 | { |
| 355 | p->save(); |
| 356 | p->setClipRect(opt.rect, Qt::IntersectClip); |
| 357 | p->translate(opt.rect.topLeft()); |
| 358 | |
| 359 | if (mView->hasActiveFilter()) |
| 360 | { |
| 361 | const auto activeColor = GitQlientStyles::getBranchColorAt(0); |
| 362 | paintGraphLane(p, LaneType::ACTIVE, false, 0, LANE_WIDTH, activeColor, activeColor, activeColor, false, |
| 363 | commit.hasChilds()); |
| 364 | } |
| 365 | else |
| 366 | { |
| 367 | if (commit.sha == CommitInfo::ZERO_SHA) |
| 368 | { |
| 369 | const auto activeColor = GitQlientStyles::getBranchColorAt(0); |
| 370 | QColor color = activeColor; |
| 371 | |
| 372 | if (mCache->pendingLocalChanges()) |
| 373 | color = gitQlientOrange; |
| 374 | |
| 375 | paintGraphLane(p, LaneType::BRANCH, false, 0, LANE_WIDTH, color, activeColor, activeColor, true, |
| 376 | commit.parentsCount() != 0); |
| 377 | } |
| 378 | else |
| 379 | { |
| 380 | const auto laneNum = commit.lanesCount(); |
| 381 | const auto activeLane = commit.getActiveLane(); |
| 382 | const auto activeColor |
| 383 | = GitQlientStyles::getBranchColorAt(activeLane % GitQlientStyles::getTotalBranchColors()); |
| 384 | auto x1 = 0; |
| 385 | auto isSet = false; |
| 386 | auto laneHeadPresent = false; |
| 387 | auto mergeColor = GitQlientStyles::getBranchColorAt((laneNum - 1) % GitQlientStyles::getTotalBranchColors()); |
| 388 | |
| 389 | for (auto i = laneNum - 1, x2 = LANE_WIDTH * laneNum; i >= 0; --i, x2 -= LANE_WIDTH) |
| 390 | { |
| 391 | x1 = x2 - LANE_WIDTH; |
| 392 | |
| 393 | auto currentLane = commit.laneAt(i); |
| 394 | |
| 395 | if (!laneHeadPresent && i < laneNum - 1) |
| 396 | { |
| 397 | auto prevLane = commit.laneAt(i + 1); |
| 398 | laneHeadPresent |
| 399 | = prevLane.isHead() || prevLane.equals(LaneType::JOIN_R) || prevLane.equals(LaneType::JOIN_L); |
| 400 | } |
| 401 | |
| 402 | if (!currentLane.equals(LaneType::EMPTY)) |
| 403 | { |
| 404 | auto color = activeColor; |
| 405 | |
| 406 | if (i != activeLane) |
| 407 | color = GitQlientStyles::getBranchColorAt(i % GitQlientStyles::getTotalBranchColors()); |
| 408 | |
| 409 | if (!isSet) |
| 410 | mergeColor = getMergeColor(currentLane, commit, i, color, isSet); |
| 411 | |
| 412 | paintGraphLane(p, currentLane, laneHeadPresent, x1, x2, color, activeColor, mergeColor, false, |
| 413 | commit.hasChilds()); |
| 414 | |
| 415 | if (mView->hasActiveFilter()) |
| 416 | break; |
| 417 | } |
| 418 | } |
| 419 | } |
| 420 | } |
| 421 | p->restore(); |
| 422 | } |
| 423 | |
| 424 | void RepositoryViewDelegate::paintLog(QPainter *p, const QStyleOptionViewItem &opt, const CommitInfo &commit, |
| 425 | const QString &text) const |
| 426 | { |
| 427 | const auto sha = commit.sha; |
| 428 | |
| 429 | if (sha.isEmpty()) |
| 430 | return; |
| 431 | |
| 432 | auto offset = 0; |
| 433 | |
| 434 | if (mGitServerCache) |
| 435 | { |
| 436 | if (const auto pr = mGitServerCache->getPullRequest(commit.sha); pr.isValid()) |
| 437 | { |
| 438 | offset = 5; |
| 439 | paintPrStatus(p, opt, offset, pr); |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | paintTagBranch(p, opt, offset, sha); |
| 444 | |
| 445 | auto newOpt = opt; |
| 446 | newOpt.rect.setX(opt.rect.x() + offset + 5); |
| 447 | |
| 448 | QFontMetrics fm(newOpt.font); |
| 449 | |
| 450 | p->setFont(newOpt.font); |
| 451 | p->setPen(GitQlientStyles::getTextColor()); |
| 452 | p->drawText(newOpt.rect, fm.elidedText(text, Qt::ElideRight, newOpt.rect.width()), |
| 453 | QTextOption(Qt::AlignLeft | Qt::AlignVCenter)); |
| 454 | } |
| 455 | |
| 456 | void RepositoryViewDelegate::paintTagBranch(QPainter *painter, QStyleOptionViewItem o, int &startPoint, |
| 457 | const QString &sha) const |
| 458 | { |
| 459 | if (mCache->hasReferences(sha) && !mView->hasActiveFilter()) |
| 460 | { |
| 461 | QVector<QString> marks; |
| 462 | QVector<QColor> colors; |
| 463 | const auto currentBranch = mGit->getCurrentBranch(); |
| 464 | |
| 465 | if (startPoint == 0) |
| 466 | startPoint = 5; |
| 467 | |
| 468 | if ((currentBranch.isEmpty() || currentBranch == "HEAD" )) |
| 469 | { |
| 470 | if (const auto ret = mGit->getLastCommit(); ret.success && sha == ret.output.trimmed()) |
| 471 | { |
| 472 | marks.append("detached" ); |
| 473 | colors.append(graphDetached); |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | const auto localBranches = mCache->getReferences(sha, References::Type::LocalBranch); |
| 478 | for (const auto &branch : localBranches) |
| 479 | { |
| 480 | if (branch == currentBranch) |
| 481 | { |
| 482 | marks.prepend(branch); |
| 483 | colors.prepend(graphCurrentBranch); |
| 484 | } |
| 485 | else |
| 486 | { |
| 487 | marks.append(branch); |
| 488 | colors.append(graphLocalBranch); |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | const auto tags = mCache->getReferences(sha, References::Type::LocalTag); |
| 493 | for (const auto &tag : tags) |
| 494 | { |
| 495 | marks.append(tag); |
| 496 | colors.append(graphTag); |
| 497 | } |
| 498 | |
| 499 | const auto remoteBranches = mCache->getReferences(sha, References::Type::RemoteBranches); |
| 500 | for (const auto &branch : remoteBranches) |
| 501 | { |
| 502 | marks.append(branch); |
| 503 | colors.append(graphRemoteBranch); |
| 504 | } |
| 505 | |
| 506 | const auto showMinimal = o.rect.width() <= MIN_VIEW_WIDTH_PX; |
| 507 | const auto mark_spacing = 5; // Space between markers in pixels |
| 508 | const auto mapEnd = marks.constEnd(); |
| 509 | |
| 510 | auto mapIt = marks.constBegin(); |
| 511 | auto colorIter = colors.constBegin(); |
| 512 | for (; mapIt != mapEnd; ++mapIt, ++colorIter) |
| 513 | { |
| 514 | const auto isCurrentSpot = *mapIt == "detached" || *mapIt == currentBranch; |
| 515 | o.font.setBold(isCurrentSpot); |
| 516 | |
| 517 | const auto nameToDisplay = showMinimal ? QString(". . ." ) : *mapIt; |
| 518 | const QFontMetrics fm(o.font); |
| 519 | const auto textBoundingRect = fm.boundingRect(nameToDisplay); |
| 520 | const int textPadding = 3; |
| 521 | const auto rectWidth = textBoundingRect.width() + 2 * textPadding; |
| 522 | const QRectF markerRect(o.rect.x() + startPoint, o.rect.y() + 2, rectWidth, ROW_HEIGHT - 4); |
| 523 | |
| 524 | painter->save(); |
| 525 | painter->setRenderHint(QPainter::Antialiasing); |
| 526 | painter->setPen(QPen(*colorIter, 2)); |
| 527 | QPainterPath path; |
| 528 | path.addRoundedRect(markerRect, 1, 1); |
| 529 | painter->fillPath(path, *colorIter); |
| 530 | painter->drawPath(path); |
| 531 | |
| 532 | // TODO: Fix this with a nicer way |
| 533 | painter->setPen(QColor(*colorIter == graphTag ? textColorBright : textColorDark)); |
| 534 | |
| 535 | painter->setFont(o.font); |
| 536 | painter->drawText(markerRect, Qt::AlignCenter, nameToDisplay); |
| 537 | painter->restore(); |
| 538 | |
| 539 | startPoint += rectWidth + mark_spacing; |
| 540 | } |
| 541 | } |
| 542 | } |
| 543 | |
| 544 | void RepositoryViewDelegate::paintPrStatus(QPainter *painter, QStyleOptionViewItem opt, int &startPoint, |
| 545 | const PullRequest &pr) const |
| 546 | { |
| 547 | QColor c; |
| 548 | |
| 549 | switch (pr.state.eState) |
| 550 | { |
| 551 | case PullRequest::HeadState::State::Failure: |
| 552 | c = GitQlientStyles::getRed(); |
| 553 | break; |
| 554 | case PullRequest::HeadState::State::Success: |
| 555 | c = GitQlientStyles::getGreen(); |
| 556 | break; |
| 557 | default: |
| 558 | case PullRequest::HeadState::State::Pending: |
| 559 | c = GitQlientStyles::getOrange(); |
| 560 | break; |
| 561 | } |
| 562 | |
| 563 | painter->save(); |
| 564 | painter->setRenderHint(QPainter::Antialiasing); |
| 565 | painter->setPen(c); |
| 566 | painter->setBrush(c); |
| 567 | painter->drawEllipse(opt.rect.x() + startPoint, opt.rect.y() + (opt.rect.height() / 2) - 5, 10, 10); |
| 568 | painter->restore(); |
| 569 | |
| 570 | startPoint += 10 + 5; |
| 571 | } |
| 572 | |