GemRepoItemDelegate.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <GemRepo/GemRepoItemDelegate.h>
  9. #include <GemRepo/GemRepoModel.h>
  10. #include <ProjectManagerDefs.h>
  11. #include <AdjustableHeaderWidget.h>
  12. #include <QEvent>
  13. #include <QPainter>
  14. #include <QMouseEvent>
  15. #include <QHeaderView>
  16. #include <QLocale>
  17. namespace O3DE::ProjectManager
  18. {
  19. GemRepoItemDelegate::GemRepoItemDelegate(QAbstractItemModel* model, AdjustableHeaderWidget* header, QObject* parent)
  20. : QStyledItemDelegate(parent)
  21. , m_model(model)
  22. , m_headerWidget(header)
  23. {
  24. m_refreshIcon = QIcon(":/Refresh.svg").pixmap(s_refreshIconSize, s_refreshIconSize);
  25. m_editIcon = QIcon(":/Edit.svg").pixmap(s_iconSize, s_iconSize);
  26. m_deleteIcon = QIcon(":/Delete.svg").pixmap(s_iconSize, s_iconSize);
  27. m_hiddenIcon = QIcon(":/Hidden.svg").pixmap(s_iconSize, s_iconSize);
  28. m_visibleIcon = QIcon(":/Visible.svg").pixmap(s_iconSize, s_iconSize);
  29. m_blueBadge = QIcon(":/BannerBlue.svg").pixmap(s_badgeWidth, s_badgeHeight);
  30. m_greenBadge = QIcon(":/BannerGreen.svg").pixmap(s_badgeWidth, s_badgeHeight);
  31. }
  32. void GemRepoItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const
  33. {
  34. if (!modelIndex.isValid())
  35. {
  36. return;
  37. }
  38. QStyleOptionViewItem options(option);
  39. initStyleOption(&options, modelIndex);
  40. painter->setRenderHint(QPainter::Antialiasing);
  41. QRect fullRect, itemRect, contentRect;
  42. CalcRects(options, fullRect, itemRect, contentRect);
  43. QFont standardFont(options.font);
  44. standardFont.setPixelSize(static_cast<int>(s_fontSize));
  45. QFontMetrics standardFontMetrics(standardFont);
  46. QFont standardBoldFont(options.font);
  47. standardBoldFont.setPixelSize(static_cast<int>(s_fontSize));
  48. standardBoldFont.setBold(true);
  49. QFontMetrics standardFontBoldMetrics(standardFont);
  50. painter->save();
  51. painter->setClipping(true);
  52. painter->setClipRect(fullRect);
  53. painter->setFont(standardFont);
  54. painter->setPen(m_textColor);
  55. // Draw background
  56. painter->fillRect(fullRect, m_backgroundColor);
  57. // Draw item background
  58. const QColor itemBackgroundColor = options.state & QStyle::State_MouseOver ? m_itemBackgroundColor.lighter(120) : m_itemBackgroundColor;
  59. painter->fillRect(itemRect, itemBackgroundColor);
  60. // Draw border
  61. if (options.state & QStyle::State_Selected)
  62. {
  63. painter->save();
  64. QPen borderPen(m_borderColor);
  65. borderPen.setWidth(s_borderWidth);
  66. painter->setPen(borderPen);
  67. painter->drawRect(itemRect);
  68. painter->restore();
  69. }
  70. int currentHorizontalOffset = CalcColumnXBounds(HeaderOrder::Name).first + s_contentMargins.left() ;
  71. // Repo name
  72. QString repoName = GemRepoModel::GetName(modelIndex);
  73. int sectionSize = m_headerWidget->m_header->sectionSize(static_cast<int>(HeaderOrder::Name)) - s_contentMargins.left();
  74. repoName = standardFontMetrics.elidedText(repoName, Qt::TextElideMode::ElideRight,
  75. sectionSize - AdjustableHeaderWidget::s_headerTextIndent);
  76. QRect repoNameRect = GetTextRect(standardFont, repoName, s_fontSize);
  77. repoNameRect.moveTo(currentHorizontalOffset,
  78. contentRect.center().y() - repoNameRect.height() / 2);
  79. repoNameRect = painter->boundingRect(repoNameRect, Qt::TextSingleLine, repoName);
  80. painter->drawText(repoNameRect, Qt::TextSingleLine, repoName);
  81. // Repo creator
  82. currentHorizontalOffset += sectionSize;
  83. sectionSize = m_headerWidget->m_header->sectionSize(static_cast<int>(HeaderOrder::Creator));
  84. QString repoCreator = GemRepoModel::GetCreator(modelIndex);
  85. repoCreator = standardFontMetrics.elidedText(repoCreator, Qt::TextElideMode::ElideRight,
  86. sectionSize - AdjustableHeaderWidget::s_headerTextIndent);
  87. QRect repoCreatorRect = GetTextRect(standardFont, repoCreator, s_fontSize);
  88. repoCreatorRect.moveTo(currentHorizontalOffset + AdjustableHeaderWidget::s_headerTextIndent,
  89. contentRect.center().y() - repoCreatorRect.height() / 2);
  90. repoCreatorRect = painter->boundingRect(repoCreatorRect, Qt::TextSingleLine, repoCreator);
  91. painter->drawText(repoCreatorRect, Qt::TextSingleLine, repoCreator);
  92. // Badge
  93. currentHorizontalOffset += sectionSize;
  94. sectionSize = m_headerWidget->m_header->sectionSize(static_cast<int>(HeaderOrder::Badge));
  95. auto badgeType = GemRepoModel::GetBadgeType(modelIndex);
  96. const QPixmap* badge = nullptr;
  97. QString badgeText;
  98. if (badgeType == GemRepoInfo::BadgeType::BlueBadge)
  99. {
  100. badge = &m_blueBadge;
  101. badgeText = tr("O3DE Official");
  102. }
  103. else if (badgeType == GemRepoInfo::BadgeType::GreenBadge)
  104. {
  105. badge = &m_greenBadge;
  106. // this text should be made dynamic at some point
  107. badgeText = tr("O3DF Recommended");
  108. }
  109. if (badge)
  110. {
  111. const QRect badgeRect = CalcBadgeRect(contentRect);
  112. painter->drawPixmap(badgeRect, m_blueBadge);
  113. painter->setFont(standardBoldFont);
  114. QRect badgeLabelRect = GetTextRect(standardBoldFont, badgeText, s_fontSize);
  115. badgeLabelRect.moveTo(currentHorizontalOffset + s_badgeLeftMargin,
  116. contentRect.center().y() - (badgeLabelRect.height() / 2) - 1);
  117. badgeLabelRect = painter->boundingRect(badgeLabelRect, Qt::TextSingleLine, badgeText);
  118. painter->drawText(badgeLabelRect, Qt::TextSingleLine, badgeText);
  119. painter->setFont(standardFont);
  120. }
  121. // Last updated
  122. currentHorizontalOffset += sectionSize;
  123. sectionSize = m_headerWidget->m_header->sectionSize(static_cast<int>(HeaderOrder::Updated));
  124. auto lastUpdated = GemRepoModel::GetLastUpdated(modelIndex);
  125. // get the month day and year in the preferred locale's format (QLocale defaults to the OS locale)
  126. QString monthDayYear = lastUpdated.toString(QLocale().dateFormat(QLocale::ShortFormat));
  127. // always show 12 hour + minutes + am/pm
  128. QString hourMinuteAMPM = lastUpdated.toString("h:mmap");
  129. QString repoUpdatedDate = QString("%1 %2").arg(monthDayYear, hourMinuteAMPM);
  130. repoUpdatedDate = standardFontMetrics.elidedText(
  131. repoUpdatedDate, Qt::TextElideMode::ElideRight,
  132. sectionSize - AdjustableHeaderWidget::s_headerTextIndent);
  133. QRect repoUpdatedDateRect = GetTextRect(standardFont, repoUpdatedDate, s_fontSize);
  134. repoUpdatedDateRect.moveTo(currentHorizontalOffset + AdjustableHeaderWidget::s_headerTextIndent,
  135. contentRect.center().y() - repoUpdatedDateRect.height() / 2);
  136. repoUpdatedDateRect = painter->boundingRect(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate);
  137. painter->drawText(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate);
  138. // Refresh button
  139. const QRect refreshButtonRect = CalcRefreshButtonRect(contentRect);
  140. painter->drawPixmap(refreshButtonRect.topLeft(), m_refreshIcon);
  141. // Visibility button
  142. const QRect visibilityButtonRect = CalcVisibilityButtonRect(contentRect);
  143. painter->drawPixmap(visibilityButtonRect, GemRepoModel::IsEnabled(modelIndex) ? m_visibleIcon : m_hiddenIcon);
  144. painter->restore();
  145. }
  146. QSize GemRepoItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const
  147. {
  148. QStyleOptionViewItem options(option);
  149. initStyleOption(&options, modelIndex);
  150. const int marginsHorizontal = s_itemMargins.left() + s_itemMargins.right() + s_contentMargins.left() + s_contentMargins.right();
  151. return QSize(marginsHorizontal + s_nameDefaultWidth + s_creatorDefaultWidth + s_buttonsDefaultWidth, s_height);
  152. }
  153. bool GemRepoItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex)
  154. {
  155. if (!modelIndex.isValid())
  156. {
  157. return false;
  158. }
  159. if (event->type() == QEvent::KeyPress)
  160. {
  161. auto keyEvent = static_cast<const QKeyEvent*>(event);
  162. if (keyEvent->key() == Qt::Key_X)
  163. {
  164. emit RemoveRepo(modelIndex);
  165. return true;
  166. }
  167. else if (keyEvent->key() == Qt::Key_R || keyEvent->key() == Qt::Key_F5)
  168. {
  169. emit RefreshRepo(modelIndex);
  170. return true;
  171. }
  172. }
  173. if (event->type() == QEvent::MouseButtonPress)
  174. {
  175. QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
  176. QRect fullRect, itemRect, contentRect;
  177. CalcRects(option, fullRect, itemRect, contentRect);
  178. const QRect visibilityButtonRect = CalcVisibilityButtonRect(contentRect);
  179. const QRect refreshButtonRect = CalcRefreshButtonRect(contentRect);
  180. if (visibilityButtonRect.contains(mouseEvent->pos()))
  181. {
  182. bool isAdded = GemRepoModel::IsEnabled(modelIndex);
  183. GemRepoModel::SetEnabled(*model, modelIndex, !isAdded);
  184. return true;
  185. }
  186. else if (refreshButtonRect.contains(mouseEvent->pos()))
  187. {
  188. emit RefreshRepo(modelIndex);
  189. return true;
  190. }
  191. }
  192. return QStyledItemDelegate::editorEvent(event, model, option, modelIndex);
  193. }
  194. void GemRepoItemDelegate::CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const
  195. {
  196. outFullRect = QRect(option.rect);
  197. outItemRect = QRect(outFullRect.adjusted(s_itemMargins.left(), s_itemMargins.top(), -s_itemMargins.right(), -s_itemMargins.bottom()));
  198. outContentRect = QRect(outItemRect.adjusted(s_contentMargins.left(), s_contentMargins.top(), -s_contentMargins.right(), -s_contentMargins.bottom()));
  199. }
  200. QRect GemRepoItemDelegate::GetTextRect(QFont& font, const QString& text, qreal fontSize) const
  201. {
  202. font.setPixelSize(static_cast<int>(fontSize));
  203. return QFontMetrics(font).boundingRect(text);
  204. }
  205. QPair<int, int> GemRepoItemDelegate::CalcColumnXBounds(HeaderOrder header) const
  206. {
  207. return m_headerWidget->CalcColumnXBounds(static_cast<int>(header));
  208. }
  209. QRect GemRepoItemDelegate::CalcBadgeRect(const QRect& contentRect) const
  210. {
  211. const auto bounds = CalcColumnXBounds(HeaderOrder::Badge);
  212. const QPoint topLeft = QPoint(bounds.first, contentRect.center().y() - s_badgeHeight / 2);
  213. return QRect(topLeft, QSize(s_badgeWidth, s_badgeHeight));
  214. }
  215. QRect GemRepoItemDelegate::CalcVisibilityButtonRect(const QRect& contentRect) const
  216. {
  217. const auto bounds = CalcColumnXBounds(HeaderOrder::Buttons);
  218. const int centerX = (bounds.first + bounds.second) / 2;
  219. const QPoint topLeft = QPoint(centerX + s_refreshIconSpacing, contentRect.center().y() - s_iconSize / 2);
  220. return QRect(topLeft, QSize(s_iconSize, s_iconSize));
  221. }
  222. QRect GemRepoItemDelegate::CalcRefreshButtonRect(const QRect& contentRect) const
  223. {
  224. const auto bounds = CalcColumnXBounds(HeaderOrder::Buttons);
  225. const int centerX = (bounds.first + bounds.second) / 2;
  226. const QPoint topLeft = QPoint(centerX - s_refreshIconSpacing - s_refreshIconSize, contentRect.center().y() - s_refreshIconSize / 2 + 1);
  227. return QRect(topLeft, QSize(s_refreshIconSize, s_refreshIconSize));
  228. }
  229. } // namespace O3DE::ProjectManager