Adds Download status info UI to Gem Catalog (#4602)

* Adds Download status info UI to Gem Catalog

Signed-off-by: nggieber <nggieber@amazon.com>

* Removed test code

Signed-off-by: nggieber <nggieber@amazon.com>

* Remove unused variable

Signed-off-by: nggieber <nggieber@amazon.com>

* Addressed PR feedback

Signed-off-by: nggieber <nggieber@amazon.com>

* Fixed Open3DEngine spelling

Signed-off-by: nggieber <nggieber@amazon.com>
This commit is contained in:
AMZN-nggieber
2021-10-13 12:52:44 -07:00
committed by GitHub
parent da3a39a6a0
commit 10ab1a369f
14 changed files with 194 additions and 32 deletions
@@ -33,7 +33,7 @@ namespace O3DE::ProjectManager
m_closeButton = new QPushButton();
m_closeButton->setFlat(true);
m_closeButton->setIcon(QIcon(":/FeatureTagClose.svg"));
m_closeButton->setIcon(QIcon(":/X.svg"));
m_closeButton->setIconSize(QSize(12, 12));
m_closeButton->setStyleSheet("QPushButton { background-color: transparent; border: 0px }");
layout->addWidget(m_closeButton);
@@ -8,6 +8,8 @@
#include "GemInfo.h"
#include <QObject>
namespace O3DE::ProjectManager
{
GemInfo::GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded)
@@ -29,17 +31,17 @@ namespace O3DE::ProjectManager
switch (platform)
{
case Android:
return "Android";
return QObject::tr("Android");
case iOS:
return "iOS";
return QObject::tr("iOS");
case Linux:
return "Linux";
return QObject::tr("Linux");
case macOS:
return "macOS";
return QObject::tr("macOS");
case Windows:
return "Windows";
return QObject::tr("Windows");
default:
return "<Unknown Platform>";
return QObject::tr("<Unknown Platform>");
}
}
@@ -48,13 +50,13 @@ namespace O3DE::ProjectManager
switch (type)
{
case Asset:
return "Asset";
return QObject::tr("Asset");
case Code:
return "Code";
return QObject::tr("Code");
case Tool:
return "Tool";
return QObject::tr("Tool");
default:
return "<Unknown Type>";
return QObject::tr("<Unknown Type>");
}
}
@@ -62,15 +64,33 @@ namespace O3DE::ProjectManager
{
switch (origin)
{
case Open3DEEngine:
return "Open 3D Engine";
case Open3DEngine:
return QObject::tr("Open 3D Engine");
case Local:
return "Local";
return QObject::tr("Local");
case Remote:
return QObject::tr("Remote");
default:
return "<Unknown Gem Origin>";
return QObject::tr("<Unknown Gem Origin>");
}
}
QString GemInfo::GetDownloadStatusString(DownloadStatus status)
{
switch (status)
{
case NotDownloaded:
return QObject::tr("Not Downloaded");
case Downloading:
return QObject::tr("Downloading");
case Downloaded:
return QObject::tr("Downloaded");
case UnknownDownloadStatus:
default:
return QObject::tr("<Unknown Download Status>");
}
};
bool GemInfo::IsPlatformSupported(Platform platform) const
{
return (m_platforms & platform);
@@ -44,13 +44,23 @@ namespace O3DE::ProjectManager
enum GemOrigin
{
Open3DEEngine = 1 << 0,
Open3DEngine = 1 << 0,
Local = 1 << 1,
NumGemOrigins = 2
Remote = 1 << 2,
NumGemOrigins = 3
};
Q_DECLARE_FLAGS(GemOrigins, GemOrigin)
static QString GetGemOriginString(GemOrigin origin);
enum DownloadStatus
{
UnknownDownloadStatus = -1,
NotDownloaded,
Downloading,
Downloaded,
};
static QString GetDownloadStatusString(DownloadStatus status);
GemInfo() = default;
GemInfo(const QString& name, const QString& creator, const QString& summary, Platforms platforms, bool isAdded);
bool IsPlatformSupported(Platform platform) const;
@@ -68,6 +78,7 @@ namespace O3DE::ProjectManager
QString m_summary = "No summary provided.";
Platforms m_platforms;
Types m_types; //! Asset and/or Code and/or Tool
DownloadStatus m_downloadStatus = UnknownDownloadStatus;
QStringList m_features;
QString m_requirement;
QString m_directoryLink;
@@ -10,6 +10,7 @@
#include <GemCatalog/GemModel.h>
#include <GemCatalog/GemSortFilterProxyModel.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <QEvent>
#include <QAbstractItemView>
#include <QPainter>
@@ -20,6 +21,7 @@
#include <QTextDocument>
#include <QAbstractTextDocumentLayout>
#include <QDesktopServices>
#include <QMovie>
namespace O3DE::ProjectManager
{
@@ -32,6 +34,11 @@ namespace O3DE::ProjectManager
AddPlatformIcon(GemInfo::Linux, ":/Linux.svg");
AddPlatformIcon(GemInfo::macOS, ":/macOS.svg");
AddPlatformIcon(GemInfo::Windows, ":/Windows.svg");
SetStatusIcon(m_notDownloadedPixmap, ":/Download.svg");
SetStatusIcon(m_unknownStatusPixmap, ":/X.svg");
m_downloadingMovie = new QMovie(":/in_progress.gif");
}
void GemItemDelegate::AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath)
@@ -41,6 +48,25 @@ namespace O3DE::ProjectManager
m_platformIcons.insert(platform, QIcon(iconPath).pixmap(static_cast<int>(static_cast<qreal>(s_platformIconSize) * aspectRatio), s_platformIconSize));
}
void GemItemDelegate::SetStatusIcon(QPixmap& m_iconPixmap, const QString& iconPath)
{
QPixmap pixmap(iconPath);
float aspectRatio = static_cast<float>(pixmap.width()) / pixmap.height();
int xScaler = s_statusIconSize;
int yScaler = s_statusIconSize;
if (aspectRatio > 1.0f)
{
yScaler = static_cast<int>(1.0f / aspectRatio * s_statusIconSize);
}
else if (aspectRatio < 1.0f)
{
xScaler = static_cast<int>(aspectRatio * s_statusIconSize);
}
m_iconPixmap = QPixmap(QIcon(iconPath).pixmap(xScaler, yScaler));
}
void GemItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const
{
if (!modelIndex.isValid())
@@ -56,6 +82,8 @@ namespace O3DE::ProjectManager
QRect fullRect, itemRect, contentRect;
CalcRects(options, fullRect, itemRect, contentRect);
QRect buttonRect = CalcButtonRect(contentRect);
QFont standardFont(options.font);
standardFont.setPixelSize(static_cast<int>(s_fontSize));
QFontMetrics standardFontMetrics(standardFont);
@@ -114,7 +142,8 @@ namespace O3DE::ProjectManager
const QRect summaryRect = CalcSummaryRect(contentRect, hasTags);
DrawText(summary, painter, summaryRect, standardFont);
DrawButton(painter, contentRect, modelIndex);
DrawDownloadStatusIcon(painter, contentRect, buttonRect, modelIndex);
DrawButton(painter, buttonRect, modelIndex);
DrawPlatformIcons(painter, contentRect, modelIndex);
DrawFeatureTags(painter, contentRect, featureTags, standardFont, summaryRect);
@@ -270,7 +299,7 @@ namespace O3DE::ProjectManager
QRect GemItemDelegate::CalcButtonRect(const QRect& contentRect) const
{
const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth - s_itemMargins.right(), contentRect.top() + contentRect.height() / 2 - s_buttonHeight / 2);
const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth, contentRect.center().y() - s_buttonHeight / 2);
const QSize size = QSize(s_buttonWidth, s_buttonHeight);
return QRect(topLeft, size);
}
@@ -378,10 +407,9 @@ namespace O3DE::ProjectManager
painter->restore();
}
void GemItemDelegate::DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const
void GemItemDelegate::DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const
{
painter->save();
const QRect buttonRect = CalcButtonRect(contentRect);
QPoint circleCenter;
if (GemModel::IsAdded(modelIndex))
@@ -427,4 +455,45 @@ namespace O3DE::ProjectManager
return QString();
}
void GemItemDelegate::DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const
{
const GemInfo::DownloadStatus downloadStatus = GemModel::GetDownloadStatus(modelIndex);
// Show no icon if gem is already downloaded
if (downloadStatus == GemInfo::DownloadStatus::Downloaded)
{
return;
}
QPixmap currentFrame;
const QPixmap* statusPixmap;
if (downloadStatus == GemInfo::DownloadStatus::Downloading)
{
if (m_downloadingMovie->state() != QMovie::Running)
{
m_downloadingMovie->start();
emit MovieStartedPlaying(m_downloadingMovie);
}
currentFrame = m_downloadingMovie->currentPixmap();
currentFrame = currentFrame.scaled(s_statusIconSize, s_statusIconSize);
statusPixmap = &currentFrame;
}
else if (downloadStatus == GemInfo::DownloadStatus::NotDownloaded)
{
statusPixmap = &m_notDownloadedPixmap;
}
else
{
statusPixmap = &m_unknownStatusPixmap;
}
QSize statusSize = statusPixmap->size();
painter->drawPixmap(
buttonRect.left() - s_statusButtonSpacing - statusSize.width(),
contentRect.center().y() - statusSize.height() / 2,
*statusPixmap);
}
} // namespace O3DE::ProjectManager
@@ -49,13 +49,13 @@ namespace O3DE::ProjectManager
// Margin and borders
inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/16, /*top=*/8, /*right=*/16, /*bottom=*/8); // Item border distances
inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/15, /*bottom=*/12); // Distances of the elements within an item to the item borders
inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/20, /*bottom=*/12); // Distances of the elements within an item to the item borders
inline constexpr static int s_borderWidth = 4;
// Button
inline constexpr static int s_buttonWidth = 55;
inline constexpr static int s_buttonHeight = 18;
inline constexpr static int s_buttonBorderRadius = 9;
inline constexpr static int s_buttonWidth = 32;
inline constexpr static int s_buttonHeight = 16;
inline constexpr static int s_buttonBorderRadius = s_buttonHeight / 2;
inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2;
inline constexpr static qreal s_buttonFontSize = 10.0;
@@ -65,6 +65,9 @@ namespace O3DE::ProjectManager
inline constexpr static int s_featureTagBorderMarginY = 3;
inline constexpr static int s_featureTagSpacing = 7;
signals:
void MovieStartedPlaying(const QMovie* playingMovie) const;
protected:
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override;
bool helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) override;
@@ -74,9 +77,10 @@ namespace O3DE::ProjectManager
QRect CalcButtonRect(const QRect& contentRect) const;
QRect CalcSummaryRect(const QRect& contentRect, bool hasTags) const;
void DrawPlatformIcons(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const;
void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const;
void DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const;
void DrawFeatureTags(QPainter* painter, const QRect& contentRect, const QStringList& featureTags, const QFont& standardFont, const QRect& summaryRect) const;
void DrawText(const QString& text, QPainter* painter, const QRect& rect, const QFont& standardFont) const;
void DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const;
QAbstractItemModel* m_model = nullptr;
@@ -85,5 +89,14 @@ namespace O3DE::ProjectManager
void AddPlatformIcon(GemInfo::Platform platform, const QString& iconPath);
inline constexpr static int s_platformIconSize = 12;
QHash<GemInfo::Platform, QPixmap> m_platformIcons;
// Status icons
void SetStatusIcon(QPixmap& m_iconPixmap, const QString& iconPath);
inline constexpr static int s_statusIconSize = 16;
inline constexpr static int s_statusButtonSpacing = 5;
QPixmap m_unknownStatusPixmap;
QPixmap m_notDownloadedPixmap;
QMovie* m_downloadingMovie = nullptr;
};
} // namespace O3DE::ProjectManager
@@ -103,11 +103,11 @@ namespace O3DE::ProjectManager
QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
columnHeaderLayout->addSpacerItem(horizontalSpacer);
QLabel* gemSelectedLabel = new QLabel(tr("Selected"));
QLabel* gemSelectedLabel = new QLabel(tr("Status"));
gemSelectedLabel->setObjectName("GemCatalogHeaderLabel");
columnHeaderLayout->addWidget(gemSelectedLabel);
columnHeaderLayout->addSpacing(65);
columnHeaderLayout->addSpacing(72);
vLayout->addLayout(columnHeaderLayout);
}
@@ -9,6 +9,8 @@
#include <GemCatalog/GemListView.h>
#include <GemCatalog/GemItemDelegate.h>
#include <QMovie>
namespace O3DE::ProjectManager
{
GemListView::GemListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent)
@@ -19,6 +21,17 @@ namespace O3DE::ProjectManager
setModel(model);
setSelectionModel(selectionModel);
setItemDelegate(new GemItemDelegate(model, this));
GemItemDelegate* itemDelegate = new GemItemDelegate(model, this);
connect(itemDelegate, &GemItemDelegate::MovieStartedPlaying, [=](const QMovie* playingMovie)
{
// Force redraw when movie is playing so animation is smooth
connect(playingMovie, &QMovie::frameChanged, this, [=]
{
this->viewport()->repaint();
});
});
setItemDelegate(itemDelegate);
}
} // namespace O3DE::ProjectManager
@@ -48,6 +48,7 @@ namespace O3DE::ProjectManager
item->setData(gemInfo.m_features, RoleFeatures);
item->setData(gemInfo.m_path, RolePath);
item->setData(gemInfo.m_requirement, RoleRequirement);
item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus);
appendRow(item);
@@ -132,6 +133,11 @@ namespace O3DE::ProjectManager
return static_cast<GemInfo::Types>(modelIndex.data(RoleTypes).toInt());
}
GemInfo::DownloadStatus GemModel::GetDownloadStatus(const QModelIndex& modelIndex)
{
return static_cast<GemInfo::DownloadStatus>(modelIndex.data(RoleDownloadStatus).toInt());
}
QString GemModel::GetSummary(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleSummary).toString();
@@ -373,6 +379,11 @@ namespace O3DE::ProjectManager
return previouslyAdded && !added;
}
void GemModel::SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status)
{
model.setData(modelIndex, status, RoleDownloadStatus);
}
bool GemModel::HasRequirement(const QModelIndex& modelIndex)
{
return !modelIndex.data(RoleRequirement).toString().isEmpty();
@@ -40,6 +40,7 @@ namespace O3DE::ProjectManager
static GemInfo::GemOrigin GetGemOrigin(const QModelIndex& modelIndex);
static GemInfo::Platforms GetPlatforms(const QModelIndex& modelIndex);
static GemInfo::Types GetTypes(const QModelIndex& modelIndex);
static GemInfo::DownloadStatus GetDownloadStatus(const QModelIndex& modelIndex);
static QString GetSummary(const QModelIndex& modelIndex);
static QString GetDirectoryLink(const QModelIndex& modelIndex);
static QString GetDocLink(const QModelIndex& modelIndex);
@@ -64,6 +65,7 @@ namespace O3DE::ProjectManager
static bool NeedsToBeRemoved(const QModelIndex& modelIndex, bool includeDependencies = false);
static bool HasRequirement(const QModelIndex& modelIndex);
static void UpdateDependencies(QAbstractItemModel& model, const QModelIndex& modelIndex);
static void SetDownloadStatus(QAbstractItemModel& model, const QModelIndex& modelIndex, GemInfo::DownloadStatus status);
bool DoGemsToBeAddedHaveRequirements() const;
bool HasDependentGemsToRemove() const;
@@ -101,7 +103,8 @@ namespace O3DE::ProjectManager
RoleFeatures,
RoleTypes,
RolePath,
RoleRequirement
RoleRequirement,
RoleDownloadStatus
};
QHash<QString, QModelIndex> m_nameToIndexMap;
@@ -668,7 +668,21 @@ namespace O3DE::ProjectManager
if (gemInfo.m_creator.contains("Open 3D Engine"))
{
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Open3DEEngine;
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Open3DEngine;
}
else if (gemInfo.m_creator.contains("Amazon Web Services"))
{
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Local;
}
else if (data.contains("origin"))
{
gemInfo.m_gemOrigin = GemInfo::GemOrigin::Remote;
}
// As long Base Open3DEngine gems are installed before first startup non-remote gems will be downloaded
if (gemInfo.m_gemOrigin != GemInfo::GemOrigin::Remote)
{
gemInfo.m_downloadStatus = GemInfo::DownloadStatus::Downloaded;
}
if (data.contains("user_tags"))