Add Initial Gem Repository Management Screen (#4172)

* Adds the gem repo screen with the UI built but with mocked data and not connected to the o3de scripts

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

* Changed name of added to enabled, disabled define, removed unused functions

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

* Added eof nl to qss

Signed-off-by: nggieber <nggieber@amazon.com>
monroegm-disable-blank-issue-2
AMZN-nggieber 4 years ago committed by GitHub
parent 59f79ad0de
commit 357e823323
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.33301 6H12.6663V14.6667H3.33301V6ZM5.33301 7.33333H6.66634V13.3333H5.33301V7.33333ZM10.6663 7.33333H9.33301V13.3333H10.6663V7.33333Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6667 2H5.33333V3.33333H2V4.66667H14V3.33333H10.6667V2Z" fill="#E9E9E9"/>
</svg>

After

Width:  |  Height:  |  Size: 430 B

@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2" y="2" width="1.33333" height="12" fill="white"/>
<rect x="2" y="12.6666" width="12" height="1.33333" fill="white"/>
<rect x="5.33301" y="9.20911" width="10.6667" height="2" transform="rotate(-45 5.33301 9.20911)" fill="white"/>
<rect x="2" y="2" width="6.66667" height="1.33333" fill="white"/>
<rect width="1.33333" height="6.66667" transform="matrix(-1 0 0 1 14 7.33337)" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 503 B

@ -35,5 +35,8 @@
<file>Backgrounds/DefaultBackground.jpg</file>
<file>Backgrounds/FtueBackground.jpg</file>
<file>FeatureTagClose.svg</file>
<file>Refresh.svg</file>
<file>Edit.svg</file>
<file>Delete.svg</file>
</qresource>
</RCC>

@ -518,3 +518,82 @@ QProgressBar::chunk {
font-size: 12px;
font-weight: 600;
}
/************** Engine **************/
#engineTab::tab-bar {
left: 60px;
}
#engineTabBar::tab {
height: 50px;
background-color: transparent;
font-weight: 400;
font-size: 18px;
min-width: 160px;
}
#engineTabBar::tab:selected {
border-bottom: 3px solid #94D2FF;
color: #94D2FF;
font-weight: 600;
}
#engineTabBar::tab:hover {
color: #94D2FF;
font-weight: 600;
}
#engineTabBar::tab:pressed {
color: #66bcfa;
}
#engineTopFrame {
background-color:#1E252F;
}
/************** Gem Repo **************/
#gemRepoHeaderLabel {
font-size: 12px;
}
#gemRepoHeaderRefreshButton {
background-color: transparent;
qproperty-flat: true;
qproperty-iconSize: 14px;
}
#gemRepoHeaderAddButton {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #888888, stop: 1.0 #555555);
qproperty-flat: true;
margin-right:30px;
min-width:120px;
max-width:120px;
min-height:24px;
max-height:24px;
border-radius: 3px;
text-align:center;
font-size:12px;
font-weight:600;
}
#gemRepoHeaderAddButton:hover {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #999999, stop: 1.0 #666666);
}
#gemRepoHeaderAddButton:pressed {
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 #555555, stop: 1.0 #777777);
}
#gemRepoHeaderTable {
background-color: transparent;
max-height: 30px;
}
#gemRepoListHeader {
background-color: transparent;
}
#gemRepoInspector {
background: #444444;
}

@ -0,0 +1,4 @@
<svg width="15" height="12" viewBox="0 0 15 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.046 5.85448C13.046 5.869 13.0436 5.66561 13.0436 5.68014H14.5278L12.3341 8.22252L10.1114 5.68014H11.6481C11.6481 5.66561 11.6513 5.869 11.6513 5.85448C11.6513 3.42833 9.77724 1.46707 7.46731 1.46707C6.42131 1.46707 5.46247 1.87385 4.73608 2.54213L3.8063 1.43801C4.79419 0.537286 6.07264 -0.000244141 7.46731 -0.000244141C10.5472 -0.000244141 13.046 2.6293 13.046 5.85448Z" fill="white"/>
<path d="M1.48184 6.14503C1.48184 6.13051 1.48428 6.3339 1.48428 6.31937H0L2.1937 3.777L4.41646 6.31937H2.87975C2.87975 6.3339 2.87651 6.13051 2.87651 6.14503C2.87651 8.57118 4.7506 10.5324 7.06053 10.5324C8.10654 10.5324 9.06537 10.1257 9.79177 9.45738L10.7215 10.5615C9.73366 11.4622 8.4552 11.9998 7.06053 11.9998C3.98063 11.9998 1.48184 9.37022 1.48184 6.14503Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 886 B

@ -0,0 +1,64 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <EngineScreenCtrl.h>
#include <GemRepo/GemRepoScreen.h>
#include <EngineSettingsScreen.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QTabWidget>
namespace O3DE::ProjectManager
{
EngineScreenCtrl::EngineScreenCtrl(QWidget* parent)
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setContentsMargins(0, 0, 0, 0);
QFrame* topBarFrameWidget = new QFrame(this);
topBarFrameWidget->setObjectName("engineTopFrame");
QHBoxLayout* topBarHLayout = new QHBoxLayout();
topBarHLayout->setContentsMargins(0, 0, 0, 0);
topBarFrameWidget->setLayout(topBarHLayout);
QTabWidget* tabWidget = new QTabWidget();
tabWidget->setObjectName("engineTab");
tabWidget->tabBar()->setObjectName("engineTabBar");
tabWidget->tabBar()->setFocusPolicy(Qt::TabFocus);
m_engineSettingsScreen = new EngineSettingsScreen();
m_gemRepoScreen = new GemRepoScreen();
tabWidget->addTab(m_engineSettingsScreen, tr("General"));
tabWidget->addTab(m_gemRepoScreen, tr("Gem Repositories"));
topBarHLayout->addWidget(tabWidget);
vLayout->addWidget(topBarFrameWidget);
setLayout(vLayout);
}
ProjectManagerScreen EngineScreenCtrl::GetScreenEnum()
{
return ProjectManagerScreen::UpdateProject;
}
QString EngineScreenCtrl::GetTabText()
{
return tr("Engine");
}
bool EngineScreenCtrl::IsTab()
{
return true;
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,34 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <ScreenWidget.h>
#endif
namespace O3DE::ProjectManager
{
QT_FORWARD_DECLARE_CLASS(EngineSettingsScreen)
QT_FORWARD_DECLARE_CLASS(GemRepoScreen)
class EngineScreenCtrl
: public ScreenWidget
{
public:
explicit EngineScreenCtrl(QWidget* parent = nullptr);
~EngineScreenCtrl() = default;
ProjectManagerScreen GetScreenEnum() override;
QString GetTabText() override;
bool IsTab() override;
EngineSettingsScreen* m_engineSettingsScreen = nullptr;
GemRepoScreen* m_gemRepoScreen = nullptr;
};
} // namespace O3DE::ProjectManager

@ -7,15 +7,16 @@
*/
#include <EngineSettingsScreen.h>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
#include <FormLineEditWidget.h>
#include <FormFolderBrowseEditWidget.h>
#include <PythonBindingsInterface.h>
#include <PathValidator.h>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
namespace O3DE::ProjectManager
{
EngineSettingsScreen::EngineSettingsScreen(QWidget* parent)
@ -78,16 +79,6 @@ namespace O3DE::ProjectManager
return ProjectManagerScreen::EngineSettings;
}
QString EngineSettingsScreen::GetTabText()
{
return tr("Engine");
}
bool EngineSettingsScreen::IsTab()
{
return true;
}
void EngineSettingsScreen::OnTextChanged()
{
// save engine settings

@ -24,8 +24,6 @@ namespace O3DE::ProjectManager
~EngineSettingsScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
QString GetTabText() override;
bool IsTab() override;
protected slots:
void OnTextChanged();

@ -0,0 +1,32 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <GemRepo/GemRepoInfo.h>
namespace O3DE::ProjectManager
{
GemRepoInfo::GemRepoInfo(
const QString& name, const QString& creator, const QString& summary, const QDateTime& lastUpdated, bool isEnabled = true)
: m_name(name)
, m_creator(creator)
, m_summary(summary)
, m_lastUpdated(lastUpdated)
, m_isEnabled(isEnabled)
{
}
bool GemRepoInfo::IsValid() const
{
return !m_name.isEmpty();
}
bool GemRepoInfo::operator<(const GemRepoInfo& gemRepoInfo) const
{
return (m_lastUpdated < gemRepoInfo.m_lastUpdated);
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,37 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QString>
#include <QDateTime>
#endif
namespace O3DE::ProjectManager
{
class GemRepoInfo
{
public:
GemRepoInfo() = default;
GemRepoInfo(const QString& name, const QString& creator, const QString& summary, const QDateTime& lastUpdated, bool isEnabled);
bool IsValid() const;
bool operator<(const GemRepoInfo& gemRepoInfo) const;
QString m_path;
QString m_name = "Unknown Gem Repo Name";
QString m_creator = "Unknown Creator";
bool m_isEnabled = false; //! Is the repo currently enabled for this engine?
QString m_summary = "No summary provided.";
QString m_directoryLink;
QString m_repoLink;
QDateTime m_lastUpdated;
};
} // namespace O3DE::ProjectManager

@ -0,0 +1,222 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <GemRepo/GemRepoItemDelegate.h>
#include <GemRepo/GemRepoModel.h>
#include <QEvent>
#include <QPainter>
#include <QMouseEvent>
namespace O3DE::ProjectManager
{
GemRepoItemDelegate::GemRepoItemDelegate(QAbstractItemModel* model, QObject* parent)
: QStyledItemDelegate(parent)
, m_model(model)
{
m_refreshIcon = QIcon(":/Refresh.svg").pixmap(s_refreshIconSize, s_refreshIconSize);
m_editIcon = QIcon(":/Edit.svg").pixmap(s_iconSize, s_iconSize);
m_deleteIcon = QIcon(":/Delete.svg").pixmap(s_iconSize, s_iconSize);
}
void GemRepoItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const
{
if (!modelIndex.isValid())
{
return;
}
QStyleOptionViewItem options(option);
initStyleOption(&options, modelIndex);
painter->setRenderHint(QPainter::Antialiasing);
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);
painter->save();
painter->setClipping(true);
painter->setClipRect(fullRect);
painter->setFont(standardFont);
painter->setPen(m_textColor);
// Draw background
painter->fillRect(fullRect, m_backgroundColor);
// Draw item background
const QColor itemBackgroundColor = options.state & QStyle::State_MouseOver ? m_itemBackgroundColor.lighter(120) : m_itemBackgroundColor;
painter->fillRect(itemRect, itemBackgroundColor);
// Draw border
if (options.state & QStyle::State_Selected)
{
painter->save();
QPen borderPen(m_borderColor);
borderPen.setWidth(s_borderWidth);
painter->setPen(borderPen);
painter->drawRect(itemRect);
painter->restore();
}
// Repo enabled
DrawButton(painter, buttonRect, modelIndex);
// Repo name
QString repoName = GemRepoModel::GetName(modelIndex);
repoName = QFontMetrics(standardFont).elidedText(repoName, Qt::TextElideMode::ElideRight, s_nameMaxWidth);
QRect repoNameRect = GetTextRect(standardFont, repoName, s_fontSize);
int currentHorizontalOffset = buttonRect.left() + s_buttonWidth + s_buttonSpacing;
repoNameRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoNameRect.height() / 2);
repoNameRect = painter->boundingRect(repoNameRect, Qt::TextSingleLine, repoName);
painter->drawText(repoNameRect, Qt::TextSingleLine, repoName);
// Rem repo creator
QString repoCreator = GemRepoModel::GetCreator(modelIndex);
repoCreator = standardFontMetrics.elidedText(repoCreator, Qt::TextElideMode::ElideRight, s_creatorMaxWidth);
QRect repoCreatorRect = GetTextRect(standardFont, repoCreator, s_fontSize);
currentHorizontalOffset += s_nameMaxWidth + s_contentSpacing;
repoCreatorRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoCreatorRect.height() / 2);
repoCreatorRect = painter->boundingRect(repoCreatorRect, Qt::TextSingleLine, repoCreator);
painter->drawText(repoCreatorRect, Qt::TextSingleLine, repoCreator);
// Repo update
QString repoUpdatedDate = GemRepoModel::GetLastUpdated(modelIndex).toString("dd/MM/yyyy hh:mmap");
repoUpdatedDate = standardFontMetrics.elidedText(repoUpdatedDate, Qt::TextElideMode::ElideRight, s_updatedMaxWidth);
QRect repoUpdatedDateRect = GetTextRect(standardFont, repoUpdatedDate, s_fontSize);
currentHorizontalOffset += s_creatorMaxWidth + s_contentSpacing;
repoUpdatedDateRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoUpdatedDateRect.height() / 2);
repoUpdatedDateRect = painter->boundingRect(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate);
painter->drawText(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate);
// Draw refresh button
painter->drawPixmap(
repoUpdatedDateRect.left() + repoUpdatedDateRect.width() + s_refreshIconSpacing,
contentRect.center().y() - s_refreshIconSize / 3, // Dividing size by 3 centers much better
m_refreshIcon);
if (options.state & QStyle::State_MouseOver)
{
DrawEditButtons(painter, contentRect);
}
painter->restore();
}
QSize GemRepoItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const
{
QStyleOptionViewItem options(option);
initStyleOption(&options, modelIndex);
int marginsHorizontal = s_itemMargins.left() + s_itemMargins.right() + s_contentMargins.left() + s_contentMargins.right();
return QSize(marginsHorizontal + s_buttonWidth + s_buttonSpacing + s_nameMaxWidth + s_creatorMaxWidth + s_updatedMaxWidth + s_contentSpacing * 3, s_height);
}
bool GemRepoItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex)
{
if (!modelIndex.isValid())
{
return false;
}
if (event->type() == QEvent::KeyPress)
{
auto keyEvent = static_cast<const QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Space)
{
const bool isAdded = GemRepoModel::IsEnabled(modelIndex);
GemRepoModel::SetEnabled(*model, modelIndex, !isAdded);
return true;
}
}
if (event->type() == QEvent::MouseButtonPress)
{
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QRect fullRect, itemRect, contentRect;
CalcRects(option, fullRect, itemRect, contentRect);
const QRect buttonRect = CalcButtonRect(contentRect);
if (buttonRect.contains(mouseEvent->pos()))
{
const bool isAdded = GemRepoModel::IsEnabled(modelIndex);
GemRepoModel::SetEnabled(*model, modelIndex, !isAdded);
return true;
}
}
return QStyledItemDelegate::editorEvent(event, model, option, modelIndex);
}
void GemRepoItemDelegate::CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const
{
outFullRect = QRect(option.rect);
outItemRect = QRect(outFullRect.adjusted(s_itemMargins.left(), s_itemMargins.top(), -s_itemMargins.right(), -s_itemMargins.bottom()));
outContentRect = QRect(outItemRect.adjusted(s_contentMargins.left(), s_contentMargins.top(), -s_contentMargins.right(), -s_contentMargins.bottom()));
}
QRect GemRepoItemDelegate::GetTextRect(QFont& font, const QString& text, qreal fontSize) const
{
font.setPixelSize(static_cast<int>(fontSize));
return QFontMetrics(font).boundingRect(text);
}
QRect GemRepoItemDelegate::CalcButtonRect(const QRect& contentRect) const
{
const QPoint topLeft = QPoint(contentRect.left(), contentRect.top() + contentRect.height() / 2 - s_buttonHeight / 2);
const QSize size = QSize(s_buttonWidth, s_buttonHeight);
return QRect(topLeft, size);
}
void GemRepoItemDelegate::DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const
{
painter->save();
QPoint circleCenter;
const bool isEnabled = GemRepoModel::IsEnabled(modelIndex);
if (isEnabled)
{
painter->setBrush(m_buttonEnabledColor);
painter->setPen(m_buttonEnabledColor);
circleCenter = buttonRect.center() + QPoint(buttonRect.width() / 2 - s_buttonBorderRadius + 1, 1);
}
else
{
circleCenter = buttonRect.center() + QPoint(-buttonRect.width() / 2 + s_buttonBorderRadius + 1, 1);
}
// Rounded rect
painter->drawRoundedRect(buttonRect, s_buttonBorderRadius, s_buttonBorderRadius);
// Circle
painter->setBrush(m_textColor);
painter->drawEllipse(circleCenter, s_buttonCircleRadius, s_buttonCircleRadius);
painter->restore();
}
void GemRepoItemDelegate::DrawEditButtons(QPainter* painter, const QRect& contentRect) const
{
painter->drawPixmap(contentRect.right() - s_iconSize * 2 - s_iconSpacing, contentRect.center().y() - s_iconSize / 2, m_editIcon);
painter->drawPixmap(contentRect.right() - s_iconSize, contentRect.center().y() - s_iconSize / 2, m_deleteIcon);
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,82 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QStyledItemDelegate>
#include <GemRepo/GemRepoInfo.h>
#endif
QT_FORWARD_DECLARE_CLASS(QAbstractItemModel)
QT_FORWARD_DECLARE_CLASS(QEvent)
namespace O3DE::ProjectManager
{
class GemRepoItemDelegate
: public QStyledItemDelegate
{
Q_OBJECT // AUTOMOC
public:
explicit GemRepoItemDelegate(QAbstractItemModel* model, QObject* parent = nullptr);
~GemRepoItemDelegate() = default;
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override;
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override;
// Colors
const QColor m_textColor = QColor("#FFFFFF");
const QColor m_backgroundColor = QColor("#333333"); // Outside of the actual repo item
const QColor m_itemBackgroundColor = QColor("#404040"); // Background color of the repo item
const QColor m_borderColor = QColor("#1E70EB");
const QColor m_buttonEnabledColor = QColor("#1E70EB");
// Item
inline constexpr static int s_height = 72; // Repo item total height
inline constexpr static qreal s_fontSize = 12.0;
// Margin and borders
inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/0, /*top=*/8, /*right=*/60, /*bottom=*/8); // Item border distances
inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/20, /*right=*/20, /*bottom=*/20); // Distances of the elements within an item to the item borders
inline constexpr static int s_borderWidth = 4;
// Content
inline constexpr static int s_contentSpacing = 5;
inline constexpr static int s_nameMaxWidth = 145;
inline constexpr static int s_creatorMaxWidth = 115;
inline constexpr static int s_updatedMaxWidth = 125;
// Button
inline constexpr static int s_buttonWidth = 32;
inline constexpr static int s_buttonHeight = 16;
inline constexpr static int s_buttonBorderRadius = 8;
inline constexpr static int s_buttonCircleRadius = s_buttonBorderRadius - 2;
inline constexpr static int s_buttonSpacing = 20;
// Icon
inline constexpr static int s_iconSize = 24;
inline constexpr static int s_iconSpacing = 16;
inline constexpr static int s_refreshIconSize = 14;
inline constexpr static int s_refreshIconSpacing = 10;
protected:
void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const;
QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const;
QRect CalcButtonRect(const QRect& contentRect) const;
void DrawButton(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const;
void DrawEditButtons(QPainter* painter, const QRect& contentRect) const;
QAbstractItemModel* m_model = nullptr;
QPixmap m_refreshIcon;
QPixmap m_editIcon;
QPixmap m_deleteIcon;
};
} // namespace O3DE::ProjectManager

@ -0,0 +1,23 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <GemRepo/GemRepoListView.h>
#include <GemRepo/GemRepoItemDelegate.h>
namespace O3DE::ProjectManager
{
GemRepoListView::GemRepoListView(QAbstractItemModel* model, QWidget* parent)
: QListView(parent)
{
setObjectName("gemRepoListView");
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
setModel(model);
setItemDelegate(new GemRepoItemDelegate(model, this));
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,28 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QListView>
#endif
QT_FORWARD_DECLARE_CLASS(QAbstractItemModel)
namespace O3DE::ProjectManager
{
class GemRepoListView
: public QListView
{
Q_OBJECT // AUTOMOC
public:
explicit GemRepoListView(QAbstractItemModel* model, QWidget* parent = nullptr);
~GemRepoListView() = default;
};
} // namespace O3DE::ProjectManager

@ -0,0 +1,94 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <GemRepo/GemRepoModel.h>
#include <QItemSelectionModel>
namespace O3DE::ProjectManager
{
GemRepoModel::GemRepoModel(QObject* parent)
: QStandardItemModel(parent)
{
m_selectionModel = new QItemSelectionModel(this, parent);
}
QItemSelectionModel* GemRepoModel::GetSelectionModel() const
{
return m_selectionModel;
}
void GemRepoModel::AddGemRepo(const GemRepoInfo& gemRepoInfo)
{
QStandardItem* item = new QStandardItem();
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
item->setData(gemRepoInfo.m_name, RoleName);
item->setData(gemRepoInfo.m_creator, RoleCreator);
item->setData(gemRepoInfo.m_summary, RoleSummary);
item->setData(gemRepoInfo.m_isEnabled, RoleIsEnabled);
item->setData(gemRepoInfo.m_directoryLink, RoleDirectoryLink);
item->setData(gemRepoInfo.m_repoLink, RoleRepoLink);
item->setData(gemRepoInfo.m_lastUpdated, RoleLastUpdated);
item->setData(gemRepoInfo.m_path, RolePath);
appendRow(item);
}
void GemRepoModel::Clear()
{
clear();
}
QString GemRepoModel::GetName(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleName).toString();
}
QString GemRepoModel::GetCreator(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleCreator).toString();
}
QString GemRepoModel::GetSummary(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleSummary).toString();
}
QString GemRepoModel::GetDirectoryLink(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleDirectoryLink).toString();
}
QString GemRepoModel::GetRepoLink(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleRepoLink).toString();
}
QDateTime GemRepoModel::GetLastUpdated(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleLastUpdated).toDateTime();
}
QString GemRepoModel::GetPath(const QModelIndex& modelIndex)
{
return modelIndex.data(RolePath).toString();
}
bool GemRepoModel::IsEnabled(const QModelIndex& modelIndex)
{
return modelIndex.data(RoleIsEnabled).toBool();
}
void GemRepoModel::SetEnabled(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isEnabled)
{
model.setData(modelIndex, isEnabled, RoleIsEnabled);
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,58 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QStandardItemModel>
#include <GemRepo/GemRepoInfo.h>
#endif
QT_FORWARD_DECLARE_CLASS(QItemSelectionModel)
namespace O3DE::ProjectManager
{
class GemRepoModel
: public QStandardItemModel
{
Q_OBJECT // AUTOMOC
public:
explicit GemRepoModel(QObject* parent = nullptr);
QItemSelectionModel* GetSelectionModel() const;
void AddGemRepo(const GemRepoInfo& gemInfo);
void Clear();
static QString GetName(const QModelIndex& modelIndex);
static QString GetCreator(const QModelIndex& modelIndex);
static QString GetSummary(const QModelIndex& modelIndex);
static QString GetDirectoryLink(const QModelIndex& modelIndex);
static QString GetRepoLink(const QModelIndex& modelIndex);
static QDateTime GetLastUpdated(const QModelIndex& modelIndex);
static QString GetPath(const QModelIndex& modelIndex);
static bool IsEnabled(const QModelIndex& modelIndex);
static void SetEnabled(QAbstractItemModel& model, const QModelIndex& modelIndex, bool isEnabled);
private:
enum UserRole
{
RoleName = Qt::UserRole,
RoleCreator,
RoleSummary,
RoleIsEnabled,
RoleDirectoryLink,
RoleRepoLink,
RoleLastUpdated,
RolePath
};
QItemSelectionModel* m_selectionModel = nullptr;
};
} // namespace O3DE::ProjectManager

@ -0,0 +1,145 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <GemRepo/GemRepoScreen.h>
#include <GemRepo/GemRepoItemDelegate.h>
#include <GemRepo/GemRepoListView.h>
#include <GemRepo/GemRepoModel.h>
#include <PythonBindingsInterface.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QTimer>
#include <QMessageBox>
#include <QLabel>
#include <QHeaderView>
#include <QTableWidget>
namespace O3DE::ProjectManager
{
GemRepoScreen::GemRepoScreen(QWidget* parent)
: ScreenWidget(parent)
{
m_gemRepoModel = new GemRepoModel(this);
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setMargin(0);
vLayout->setSpacing(0);
setLayout(vLayout);
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setMargin(0);
hLayout->setSpacing(0);
vLayout->addLayout(hLayout);
hLayout->addSpacing(60);
m_gemRepoInspector = new QFrame(this);
m_gemRepoInspector->setObjectName(tr("gemRepoInspector"));
m_gemRepoInspector->setFixedWidth(240);
QVBoxLayout* middleVLayout = new QVBoxLayout();
middleVLayout->setMargin(0);
middleVLayout->setSpacing(0);
middleVLayout->addSpacing(30);
QHBoxLayout* topMiddleHLayout = new QHBoxLayout();
topMiddleHLayout->setMargin(0);
topMiddleHLayout->setSpacing(0);
m_lastAllUpdateLabel = new QLabel(tr("Last Updated: Never"), this);
m_lastAllUpdateLabel->setObjectName("gemRepoHeaderLabel");
topMiddleHLayout->addWidget(m_lastAllUpdateLabel);
topMiddleHLayout->addSpacing(20);
m_AllUpdateButton = new QPushButton(QIcon(":/Refresh.svg"), tr("Update All"), this);
m_AllUpdateButton->setObjectName("gemRepoHeaderRefreshButton");
topMiddleHLayout->addWidget(m_AllUpdateButton);
topMiddleHLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum));
m_AddRepoButton = new QPushButton(tr("Add Repository"), this);
m_AddRepoButton->setObjectName("gemRepoHeaderAddButton");
topMiddleHLayout->addWidget(m_AddRepoButton);
middleVLayout->addLayout(topMiddleHLayout);
middleVLayout->addSpacing(30);
// Create a QTableWidget just for its header
// Using a seperate model allows the setup of a header exactly as needed
m_gemRepoHeaderTable = new QTableWidget(this);
m_gemRepoHeaderTable->setObjectName("gemRepoHeaderTable");
m_gemRepoListHeader = m_gemRepoHeaderTable->horizontalHeader();
m_gemRepoListHeader->setObjectName("gemRepoListHeader");
m_gemRepoListHeader->setSectionResizeMode(QHeaderView::ResizeMode::Fixed);
// Insert columns so the header labels will show up
m_gemRepoHeaderTable->insertColumn(0);
m_gemRepoHeaderTable->insertColumn(1);
m_gemRepoHeaderTable->insertColumn(2);
m_gemRepoHeaderTable->insertColumn(3);
m_gemRepoHeaderTable->setHorizontalHeaderLabels({ tr("Enabled"), tr("Repository Name"), tr("Creator"), tr("Updated") });
const int headerExtraMargin = 10;
m_gemRepoListHeader->resizeSection(0, GemRepoItemDelegate::s_buttonWidth + GemRepoItemDelegate::s_buttonSpacing - 3);
m_gemRepoListHeader->resizeSection(1, GemRepoItemDelegate::s_nameMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin);
m_gemRepoListHeader->resizeSection(2, GemRepoItemDelegate::s_creatorMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin);
m_gemRepoListHeader->resizeSection(3, GemRepoItemDelegate::s_updatedMaxWidth + GemRepoItemDelegate::s_contentSpacing - headerExtraMargin);
// Required to set stylesheet in code as it will not be respected if set in qss
m_gemRepoHeaderTable->horizontalHeader()->setStyleSheet("QHeaderView::section { background-color:transparent; color:white; font-size:12px; text-align:left; border-style:none; }");
middleVLayout->addWidget(m_gemRepoHeaderTable);
m_gemRepoListView = new GemRepoListView(m_gemRepoModel, this);
middleVLayout->addWidget(m_gemRepoListView);
hLayout->addLayout(middleVLayout);
hLayout->addWidget(m_gemRepoInspector);
Reinit();
}
void GemRepoScreen::Reinit()
{
m_gemRepoModel->clear();
FillModel();
// Select the first entry after everything got correctly sized
QTimer::singleShot(200, [=]{
QModelIndex firstModelIndex = m_gemRepoListView->model()->index(0,0);
m_gemRepoListView->selectionModel()->select(firstModelIndex, QItemSelectionModel::ClearAndSelect);
});
}
void GemRepoScreen::FillModel()
{
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> allGemRepoInfosResult = PythonBindingsInterface::Get()->GetAllGemRepoInfos();
if (allGemRepoInfosResult.IsSuccess())
{
// Add all available repos to the model
const QVector<GemRepoInfo> allGemRepoInfos = allGemRepoInfosResult.GetValue();
for (const GemRepoInfo& gemRepoInfo : allGemRepoInfos)
{
m_gemRepoModel->AddGemRepo(gemRepoInfo);
}
}
else
{
QMessageBox::critical(this, tr("Operation failed"), QString("Cannot retrieve gem repos for engine.\n\nError:\n%2").arg(allGemRepoInfosResult.GetError().c_str()));
}
}
ProjectManagerScreen GemRepoScreen::GetScreenEnum()
{
return ProjectManagerScreen::GemRepos;
}
} // namespace O3DE::ProjectManager

@ -0,0 +1,50 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <ScreenWidget.h>
#endif
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QHeaderView)
QT_FORWARD_DECLARE_CLASS(QTableWidget)
namespace O3DE::ProjectManager
{
QT_FORWARD_DECLARE_CLASS(GemRepoListView)
QT_FORWARD_DECLARE_CLASS(GemRepoModel)
class GemRepoScreen
: public ScreenWidget
{
public:
explicit GemRepoScreen(QWidget* parent = nullptr);
~GemRepoScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
void Reinit();
GemRepoModel* GetGemRepoModel() const { return m_gemRepoModel; }
private:
void FillModel();
QTableWidget* m_gemRepoHeaderTable = nullptr;
QHeaderView* m_gemRepoListHeader = nullptr;
GemRepoListView* m_gemRepoListView = nullptr;
QFrame* m_gemRepoInspector = nullptr;
GemRepoModel* m_gemRepoModel = nullptr;
QLabel* m_lastAllUpdateLabel;
QPushButton* m_AllUpdateButton;
QPushButton* m_AddRepoButton;
};
} // namespace O3DE::ProjectManager

@ -22,7 +22,7 @@ namespace O3DE::ProjectManager
QVector<ProjectManagerScreen> screenEnums =
{
ProjectManagerScreen::Projects,
ProjectManagerScreen::EngineSettings,
ProjectManagerScreen::Engine,
ProjectManagerScreen::CreateProject,
ProjectManagerScreen::UpdateProject
};

@ -912,4 +912,45 @@ namespace O3DE::ProjectManager
return AZ::Success(AZStd::move(templates));
}
}
GemRepoInfo PythonBindings::GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath)
{
/* Placeholder Logic */
(void)path;
(void)pyEnginePath;
return GemRepoInfo();
}
//#define MOCK_GEM_REPO_INFO true
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> PythonBindings::GetAllGemRepoInfos()
{
QVector<GemRepoInfo> gemRepos;
#ifndef MOCK_GEM_REPO_INFO
auto result = ExecuteWithLockErrorHandling(
[&]
{
/* Placeholder Logic, o3de scripts need method added
*
for (auto path : m_manifest.attr("get_gem_repos")())
{
gemRepos.push_back(GemRepoInfoFromPath(path, pybind11::none()));
}
*
*/
});
if (!result.IsSuccess())
{
return AZ::Failure<AZStd::string>(result.GetError().c_str());
}
#else
gemRepos.push_back(GemRepoInfo("JohnCreates", "John Smith", "", QDateTime(QDate(2021, 8, 31), QTime(11, 57)), true));
gemRepos.push_back(GemRepoInfo("JanesGems", "Jane Doe", "", QDateTime(QDate(2021, 9, 10), QTime(18, 23)), false));
#endif // MOCK_GEM_REPO_INFO
std::sort(gemRepos.begin(), gemRepos.end());
return AZ::Success(AZStd::move(gemRepos));
}
}

@ -56,12 +56,16 @@ namespace O3DE::ProjectManager
// ProjectTemplate
AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates(const QString& projectPath = {}) override;
// Gem Repos
AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() override;
private:
AZ_DISABLE_COPY_MOVE(PythonBindings);
AZ::Outcome<void, AZStd::string> ExecuteWithLockErrorHandling(AZStd::function<void()> executionCallback);
bool ExecuteWithLock(AZStd::function<void()> executionCallback);
GemInfo GemInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath);
GemRepoInfo GemRepoInfoFromPath(pybind11::handle path, pybind11::handle pyEnginePath);
ProjectInfo ProjectInfoFromPath(pybind11::handle path);
ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath);
bool RegisterThisEngine();

@ -17,6 +17,7 @@
#include <GemCatalog/GemInfo.h>
#include <ProjectInfo.h>
#include <ProjectTemplateInfo.h>
#include <GemRepo/GemRepoInfo.h>
namespace O3DE::ProjectManager
{
@ -56,14 +57,14 @@ namespace O3DE::ProjectManager
/**
* Get info about a Gem
* @param path the absolute path to the Gem
* @param projectPath the absolute path to the Gem
* @return an outcome with GemInfo on success
*/
virtual AZ::Outcome<GemInfo> GetGemInfo(const QString& path, const QString& projectPath = {}) = 0;
/**
* Get all available gem infos. This concatenates gems registered by the engine and the project.
* @param path The absolute path to the project.
* @param projectPath The absolute path to the project.
* @return A list of gem infos.
*/
virtual AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemInfos(const QString& projectPath) = 0;
@ -155,6 +156,14 @@ namespace O3DE::ProjectManager
* @return an outcome with ProjectTemplateInfos on success
*/
virtual AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates(const QString& projectPath = {}) = 0;
// Gem Repos
/**
* Get all available gem repo infos. Gathers all repos registered with the engine.
* @return A list of gem repo infos.
*/
virtual AZ::Outcome<QVector<GemRepoInfo>, AZStd::string> GetAllGemRepoInfos() = 0;
};
using PythonBindingsInterface = AZ::Interface<IPythonBindings>;

@ -23,7 +23,9 @@ namespace O3DE::ProjectManager
Projects,
UpdateProject,
UpdateProjectSettings,
EngineSettings
Engine,
EngineSettings,
GemRepos
};
static QHash<QString, ProjectManagerScreen> s_ProjectManagerStringNames = {
@ -34,7 +36,9 @@ namespace O3DE::ProjectManager
{ "Projects", ProjectManagerScreen::Projects},
{ "UpdateProject", ProjectManagerScreen::UpdateProject},
{ "UpdateProjectSettings", ProjectManagerScreen::UpdateProjectSettings},
{ "EngineSettings", ProjectManagerScreen::EngineSettings}
{ "Engine", ProjectManagerScreen::Engine},
{ "EngineSettings", ProjectManagerScreen::EngineSettings},
{ "GemRepos", ProjectManagerScreen::GemRepos}
};
// need to define qHash for ProjectManagerScreen when using scoped enums

@ -13,7 +13,9 @@
#include <GemCatalog/GemCatalogScreen.h>
#include <ProjectsScreen.h>
#include <UpdateProjectSettingsScreen.h>
#include <EngineScreenCtrl.h>
#include <EngineSettingsScreen.h>
#include <GemRepo/GemRepoScreen.h>
namespace O3DE::ProjectManager
{
@ -41,9 +43,15 @@ namespace O3DE::ProjectManager
case (ProjectManagerScreen::UpdateProjectSettings):
newScreen = new UpdateProjectSettingsScreen(parent);
break;
case (ProjectManagerScreen::Engine):
newScreen = new EngineScreenCtrl(parent);
break;
case (ProjectManagerScreen::EngineSettings):
newScreen = new EngineSettingsScreen(parent);
break;
case (ProjectManagerScreen::GemRepos):
newScreen = new GemRepoScreen(parent);
break;
case (ProjectManagerScreen::Empty):
default:
newScreen = new ScreenWidget(parent);

@ -56,6 +56,8 @@ set(FILES
Source/ProjectsScreen.cpp
Source/ProjectSettingsScreen.h
Source/ProjectSettingsScreen.cpp
Source/EngineScreenCtrl.h
Source/EngineScreenCtrl.cpp
Source/EngineSettingsScreen.h
Source/EngineSettingsScreen.cpp
Source/ProjectButtonWidget.h
@ -98,4 +100,14 @@ set(FILES
Source/GemCatalog/GemRequirementListView.cpp
Source/GemCatalog/GemSortFilterProxyModel.h
Source/GemCatalog/GemSortFilterProxyModel.cpp
Source/GemRepo/GemRepoScreen.h
Source/GemRepo/GemRepoScreen.cpp
Source/GemRepo/GemRepoInfo.h
Source/GemRepo/GemRepoInfo.cpp
Source/GemRepo/GemRepoItemDelegate.h
Source/GemRepo/GemRepoItemDelegate.cpp
Source/GemRepo/GemRepoListView.h
Source/GemRepo/GemRepoListView.cpp
Source/GemRepo/GemRepoModel.h
Source/GemRepo/GemRepoModel.cpp
)

Loading…
Cancel
Save