Merge branch 'main' into Prism/gem-db
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.02554 10L9 15.0362V18L0 9L9 0V2.98885L4 8H18V10H4.02554Z" fill="#1e70eb"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 230 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 17L15 14H9L12 17Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 190 B |
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f82f22df64b93d4bec91e56b60efa3d5ce2915ce388a2dc627f1ab720678e3d5
|
||||
size 334987
|
||||
oid sha256:4a5881b8d6cfbc4ceefb14ab96844484fe19407ee030824768f9fcce2f729d35
|
||||
size 2949
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8358f4dad9878c662b9819b2b346622af691eb45f8eddc28fff79a50650ae6cf
|
||||
size 2503
|
||||
@@ -7,6 +7,7 @@
|
||||
<file>AddOffset.svg</file>
|
||||
<file>AddOffset_Hover.svg</file>
|
||||
<file>ArrowBack.svg</file>
|
||||
<file>ArrowBack_Hover.svg</file>
|
||||
<file>build.svg</file>
|
||||
<file>FolderOffset.svg</file>
|
||||
<file>FolderOffset_Hover.svg</file>
|
||||
@@ -18,6 +19,7 @@
|
||||
<file>Linux.svg</file>
|
||||
<file>macOS.svg</file>
|
||||
<file>DefaultProjectImage.png</file>
|
||||
<file>DefaultTemplate.png</file>
|
||||
<file>ArrowDownLine.svg</file>
|
||||
<file>ArrowUpLine.svg</file>
|
||||
<file>o3de.svg</file>
|
||||
@@ -26,5 +28,8 @@
|
||||
<file>Backgrounds/FirstTimeBackgroundImage.jpg</file>
|
||||
<file>ArrowDownLine.svg</file>
|
||||
<file>ArrowUpLine.svg</file>
|
||||
<file>CarrotArrowDown.svg</file>
|
||||
<file>Summary.svg</file>
|
||||
<file>WindowClose.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -115,7 +115,7 @@ QTabBar::tab:pressed
|
||||
/************** General (Modal windows) **************/
|
||||
|
||||
#header {
|
||||
background-color:#111111;
|
||||
background-color:#111111;
|
||||
min-height:80px;
|
||||
max-height:80px;
|
||||
}
|
||||
@@ -136,11 +136,9 @@ QTabBar::tab:pressed
|
||||
#header QPushButton:focus {
|
||||
border:none;
|
||||
}
|
||||
#header QPushButton:hover {
|
||||
background:#333333 url(:/ArrowBack.svg) no-repeat center;
|
||||
}
|
||||
#header QPushButton:hover,
|
||||
#header QPushButton:pressed {
|
||||
background:#222222 url(:/ArrowBack.svg) no-repeat center;
|
||||
background:transparent url(:/ArrowBack_Hover.svg) no-repeat center;
|
||||
}
|
||||
|
||||
#headerTitle {
|
||||
@@ -172,8 +170,8 @@ QTabBar::tab:pressed
|
||||
|
||||
#footer > QPushButton {
|
||||
qproperty-flat: true;
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #0095f2, stop: 1.0 #1e70eb);
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #0095f2, stop: 1.0 #1e70eb);
|
||||
border-radius: 3px;
|
||||
min-height: 28px;
|
||||
max-height: 28px;
|
||||
@@ -181,26 +179,26 @@ QTabBar::tab:pressed
|
||||
margin-right:30px;
|
||||
}
|
||||
#footer > QPushButton:hover {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #10A5f2, stop: 1.0 #2e80eb);
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #10A5f2, stop: 1.0 #2e80eb);
|
||||
}
|
||||
#footer > QPushButton:pressed {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #0085e2, stop: 1.0 #0e60db);
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #0085e2, stop: 1.0 #0e60db);
|
||||
}
|
||||
|
||||
#footer > QPushButton[secondary="true"] {
|
||||
margin-right: 10px;
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #888888, stop: 1.0 #555555);
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #888888, stop: 1.0 #555555);
|
||||
}
|
||||
#footer > QPushButton[secondary="true"]:hover {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #999999, stop: 1.0 #666666);
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #999999, stop: 1.0 #666666);
|
||||
}
|
||||
#footer > QPushButton[secondary="true"]:pressed {
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #555555, stop: 1.0 #777777);
|
||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #555555, stop: 1.0 #777777);
|
||||
}
|
||||
|
||||
/************** Project Settings **************/
|
||||
@@ -210,9 +208,6 @@ QTabBar::tab:pressed
|
||||
|
||||
#projectTemplate {
|
||||
margin: 55px 0 0 50px;
|
||||
max-width: 780px;
|
||||
min-height:200px;
|
||||
max-height:200px;
|
||||
}
|
||||
#projectTemplateLabel {
|
||||
font-size:16px;
|
||||
@@ -227,11 +222,67 @@ QTabBar::tab:pressed
|
||||
|
||||
#projectTemplateDetails {
|
||||
background-color:#444444;
|
||||
max-width:240px;
|
||||
max-width:20%;
|
||||
min-width:240px;
|
||||
margin-left:30px;
|
||||
}
|
||||
|
||||
#projectTemplateDetails #displayName,
|
||||
#projectTemplateDetails #includedGemsTitle {
|
||||
font-size:18px;
|
||||
}
|
||||
|
||||
#projectTemplateDetails #moreGems {
|
||||
font-size:14px;
|
||||
margin-top:20px;
|
||||
}
|
||||
|
||||
#projectTemplateDetails #includedGemsTitle {
|
||||
margin-top:5px;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
#projectTemplateDetails #summary {
|
||||
padding-bottom:0px;
|
||||
border-bottom:2px solid #555555;
|
||||
min-height:80px;
|
||||
qproperty-alignment: AlignTop;
|
||||
}
|
||||
|
||||
#projectTemplateDetails #browseCatalog {
|
||||
margin:5px 0px 15px 0px;
|
||||
}
|
||||
|
||||
#projectTemplate QPushButton {
|
||||
qproperty-flat: true;
|
||||
min-width: 96px;
|
||||
max-width: 96px;
|
||||
min-height: 160px;
|
||||
max-height: 160px;
|
||||
}
|
||||
#projectTemplate #templateLabel {
|
||||
qproperty-alignment: AlignCenter;
|
||||
}
|
||||
#projectTemplate QPushButton #templateImage {
|
||||
border:3px solid transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
#projectTemplate QPushButton[Checked="true"] #templateImage {
|
||||
border:3px solid #1e70eb;
|
||||
}
|
||||
#projectTemplate QPushButton[Checked="true"] #templateLabel {
|
||||
font-weight:bold;
|
||||
}
|
||||
#projectTemplate QPushButton:hover {
|
||||
background-color: #444444;
|
||||
}
|
||||
#projectTemplate QPushButton:focus {
|
||||
outline: none;
|
||||
border:none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#projectSettingsTab::tab-bar {
|
||||
left: 60px;
|
||||
}
|
||||
@@ -311,6 +362,7 @@ QTabBar::tab:pressed
|
||||
#projectButton > #labelButton {
|
||||
border:1px solid white;
|
||||
}
|
||||
|
||||
#projectButton > #labelButton:hover,
|
||||
#projectButton > #labelButton:pressed {
|
||||
border:1px solid #1e70eb;
|
||||
@@ -350,12 +402,38 @@ QTabBar::tab:pressed
|
||||
max-height:278px;
|
||||
}
|
||||
|
||||
QProgressBar {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
padding: 0px;
|
||||
min-height: 14px;
|
||||
font-size: 2px;
|
||||
}
|
||||
|
||||
QProgressBar::chunk {
|
||||
background-color: #1E70EB;
|
||||
}
|
||||
|
||||
/************** Gem Catalog **************/
|
||||
|
||||
#GemCatalogTitle {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#GemCatalogCart {
|
||||
background-color: #555555;
|
||||
}
|
||||
|
||||
#GemCatalogCartCountLabel {
|
||||
font-size: 12px;
|
||||
background-color: #4285F4;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#GemCatalogCartOverlaySectionLabel {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/************** Gem Catalog (Inspector) **************/
|
||||
|
||||
#GemCatalogInspector {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5714 6.7619H16.7619C16.7619 4.13333 14.6286 2 12 2C9.37145 2 7.23811 4.13333 7.23811 6.7619H3.42859V22H20.5714V6.7619ZM12 3.90476C13.581 3.90476 14.8572 5.18095 14.8572 6.7619H9.14287C9.14287 5.18095 10.4191 3.90476 12 3.90476ZM13.7997 11.7424L12.0288 9.24095H11.9712L10.2003 11.7424H13.7997ZM5.00002 12.4335L10.9158 19.4063L9.03253 12.4335H5.00002ZM10.14 12.4335L11.9356 19.5147C11.9548 19.5189 11.974 19.521 11.9959 19.521C12.0178 19.521 12.0398 19.5189 12.0617 19.5147L13.86 12.4335H10.14ZM14.9675 12.4335H18.8547H19L13.0842 19.398L14.9675 12.4335ZM8.26457 9.24095H11.075L9.52787 11.4253L8.26457 9.24095ZM8.83789 11.7424L7.5193 9.46191L5.01921 11.7424H5.14804H8.83789ZM18.9808 11.7424H18.8547H15.1512L16.4286 9.41397L18.9808 11.7424ZM15.6558 9.24095H12.9252L14.4647 11.4146L15.6558 9.24095Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 966 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.5507 19.5506L19.5213 2.58008L21.289 4.34785L4.31846 21.3184L2.5507 19.5506Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.28706 2.65015L21.2576 19.6207L19.4899 21.3885L2.51929 4.41791L4.28706 2.65015Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 394 B |
@@ -15,6 +15,7 @@
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <NewProjectSettingsScreen.h>
|
||||
#include <ScreenHeaderWidget.h>
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
@@ -41,24 +42,33 @@ namespace O3DE::ProjectManager
|
||||
|
||||
m_stack = new QStackedWidget(this);
|
||||
m_stack->setObjectName("body");
|
||||
m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
|
||||
m_stack->addWidget(new NewProjectSettingsScreen());
|
||||
m_gemCatalog = new GemCatalogScreen();
|
||||
m_stack->addWidget(m_gemCatalog);
|
||||
m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding));
|
||||
|
||||
m_newProjectSettingsScreen = new NewProjectSettingsScreen(this);
|
||||
m_stack->addWidget(m_newProjectSettingsScreen);
|
||||
|
||||
m_gemCatalogScreen = new GemCatalogScreen(this);
|
||||
m_stack->addWidget(m_gemCatalogScreen);
|
||||
vLayout->addWidget(m_stack);
|
||||
|
||||
QDialogButtonBox* backNextButtons = new QDialogButtonBox();
|
||||
backNextButtons->setObjectName("footer");
|
||||
vLayout->addWidget(backNextButtons);
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox();
|
||||
buttons->setObjectName("footer");
|
||||
vLayout->addWidget(buttons);
|
||||
|
||||
m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole);
|
||||
m_backButton->setProperty("secondary", true);
|
||||
m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole);
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
connect(m_newProjectSettingsScreen, &ScreenWidget::ChangeScreenRequest, this, &CreateProjectCtrl::OnChangeScreenRequest);
|
||||
|
||||
connect(m_backButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton);
|
||||
connect(m_nextButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleNextButton);
|
||||
m_secondaryButton = buttons->addButton(tr("Back"), QDialogButtonBox::RejectRole);
|
||||
m_secondaryButton->setProperty("secondary", true);
|
||||
m_secondaryButton->setVisible(false);
|
||||
connect(m_secondaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleSecondaryButton);
|
||||
|
||||
Update();
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
m_primaryButton = buttons->addButton(tr("Create Project"), QDialogButtonBox::ApplyRole);
|
||||
connect(m_primaryButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandlePrimaryButton);
|
||||
|
||||
setLayout(vLayout);
|
||||
}
|
||||
|
||||
@@ -80,8 +90,10 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (m_stack->currentIndex() > 0)
|
||||
{
|
||||
m_stack->setCurrentIndex(m_stack->currentIndex() - 1);
|
||||
Update();
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
PreviousScreen();
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -89,70 +101,123 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::HandleNextButton()
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
void CreateProjectCtrl::HandleSecondaryButton()
|
||||
{
|
||||
ScreenWidget* currentScreen = reinterpret_cast<ScreenWidget*>(m_stack->currentWidget());
|
||||
ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum();
|
||||
|
||||
if (screenEnum == ProjectManagerScreen::NewProjectSettings)
|
||||
if (m_stack->currentIndex() > 0)
|
||||
{
|
||||
auto newProjectScreen = reinterpret_cast<NewProjectSettingsScreen*>(currentScreen);
|
||||
if (newProjectScreen)
|
||||
{
|
||||
if (!newProjectScreen->Validate())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Invalid project settings"), tr("Invalid project settings"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_projectInfo = newProjectScreen->GetProjectInfo();
|
||||
m_projectTemplatePath = newProjectScreen->GetProjectTemplatePath();
|
||||
|
||||
// The next page is the gem catalog. Gather the available gems that will be shown in the gem catalog.
|
||||
m_gemCatalog->ReinitForProject(m_projectInfo.m_path, /*isNewProject=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stack->currentIndex() != m_stack->count() - 1)
|
||||
{
|
||||
m_stack->setCurrentIndex(m_stack->currentIndex() + 1);
|
||||
Update();
|
||||
// return to Project Settings page
|
||||
PreviousScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo);
|
||||
// Configure Gems
|
||||
NextScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::Update()
|
||||
{
|
||||
if (m_stack->currentWidget() == m_gemCatalogScreen)
|
||||
{
|
||||
m_header->setSubTitle(tr("Configure project with Gems"));
|
||||
m_secondaryButton->setVisible(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setSubTitle(tr("Enter Project Details"));
|
||||
m_secondaryButton->setVisible(true);
|
||||
m_secondaryButton->setText(tr("Configure Gems"));
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::OnChangeScreenRequest(ProjectManagerScreen screen)
|
||||
{
|
||||
if (screen == ProjectManagerScreen::GemCatalog)
|
||||
{
|
||||
HandleSecondaryButton();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit ChangeScreenRequest(screen);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::NextScreen()
|
||||
{
|
||||
if (m_stack->currentIndex() < m_stack->count())
|
||||
{
|
||||
if(CurrentScreenIsValid())
|
||||
{
|
||||
m_stack->setCurrentIndex(m_stack->currentIndex() + 1);
|
||||
|
||||
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
|
||||
m_gemCatalogScreen->ReinitForProject(projectTemplatePath + "/Template", /*isNewProject=*/true);
|
||||
|
||||
Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::PreviousScreen()
|
||||
{
|
||||
// we don't require the current screen to be valid when moving back
|
||||
if (m_stack->currentIndex() > 0)
|
||||
{
|
||||
m_stack->setCurrentIndex(m_stack->currentIndex() - 1);
|
||||
Update();
|
||||
}
|
||||
}
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
void CreateProjectCtrl::HandlePrimaryButton()
|
||||
{
|
||||
CreateProject();
|
||||
}
|
||||
|
||||
bool CreateProjectCtrl::CurrentScreenIsValid()
|
||||
{
|
||||
if (m_stack->currentWidget() == m_newProjectSettingsScreen)
|
||||
{
|
||||
return m_newProjectSettingsScreen->Validate();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::CreateProject()
|
||||
{
|
||||
if (m_newProjectSettingsScreen->Validate())
|
||||
{
|
||||
ProjectInfo projectInfo = m_newProjectSettingsScreen->GetProjectInfo();
|
||||
QString projectTemplatePath = m_newProjectSettingsScreen->GetProjectTemplatePath();
|
||||
|
||||
auto result = PythonBindingsInterface::Get()->CreateProject(projectTemplatePath, projectInfo);
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
// automatically register the project
|
||||
PythonBindingsInterface::Get()->AddProject(m_projectInfo.m_path);
|
||||
PythonBindingsInterface::Get()->AddProject(projectInfo.m_path);
|
||||
|
||||
// adding gems is not implemented yet because we don't know what targets to add or how to add them
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
m_gemCatalogScreen->EnableDisableGemsForProject(projectInfo.m_path);
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
projectInfo.m_needsBuild = true;
|
||||
emit NotifyBuildProject(projectInfo);
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(this, tr("Project creation failed"), tr("Failed to create project."));
|
||||
}
|
||||
|
||||
// Enable/disable gems for the newly created project.
|
||||
m_gemCatalog->EnableDisableGemsForProject(m_projectInfo.m_path);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::Update()
|
||||
{
|
||||
ScreenWidget* currentScreen = reinterpret_cast<ScreenWidget*>(m_stack->currentWidget());
|
||||
if (currentScreen && currentScreen->GetScreenEnum() == ProjectManagerScreen::GemCatalog)
|
||||
{
|
||||
m_header->setTitle(tr("Create Project"));
|
||||
m_header->setSubTitle(tr("Configure project with Gems"));
|
||||
m_nextButton->setText(tr("Create Project"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_header->setTitle(tr("Create Project"));
|
||||
m_header->setSubTitle(tr("Enter Project Details"));
|
||||
m_nextButton->setText(tr("Next"));
|
||||
QMessageBox::warning(this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ScreenWidget.h>
|
||||
#include <ProjectInfo.h>
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#endif
|
||||
|
||||
// due to current limitations, customizing template Gems is disabled
|
||||
#define TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QStackedWidget)
|
||||
QT_FORWARD_DECLARE_CLASS(QPushButton)
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
@@ -24,6 +26,8 @@ QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
QT_FORWARD_DECLARE_CLASS(ScreenHeader)
|
||||
QT_FORWARD_DECLARE_CLASS(NewProjectSettingsScreen)
|
||||
QT_FORWARD_DECLARE_CLASS(GemCatalogScreen)
|
||||
|
||||
class CreateProjectCtrl
|
||||
: public ScreenWidget
|
||||
@@ -36,21 +40,37 @@ namespace O3DE::ProjectManager
|
||||
|
||||
protected slots:
|
||||
void HandleBackButton();
|
||||
void HandleNextButton();
|
||||
void HandlePrimaryButton();
|
||||
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
void OnChangeScreenRequest(ProjectManagerScreen screen);
|
||||
void HandleSecondaryButton();
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
private:
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
void Update();
|
||||
void NextScreen();
|
||||
void PreviousScreen();
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
QStackedWidget* m_stack;
|
||||
ScreenHeader* m_header;
|
||||
bool CurrentScreenIsValid();
|
||||
void CreateProject();
|
||||
|
||||
QPushButton* m_backButton;
|
||||
QPushButton* m_nextButton;
|
||||
QStackedWidget* m_stack = nullptr;
|
||||
ScreenHeader* m_header = nullptr;
|
||||
|
||||
QPushButton* m_primaryButton = nullptr;
|
||||
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
QPushButton* m_secondaryButton = nullptr;
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
|
||||
QString m_projectTemplatePath;
|
||||
ProjectInfo m_projectInfo;
|
||||
|
||||
GemCatalogScreen* m_gemCatalog = nullptr;
|
||||
|
||||
NewProjectSettingsScreen* m_newProjectSettingsScreen = nullptr;
|
||||
GemCatalogScreen* m_gemCatalogScreen = nullptr;
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -10,24 +10,229 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzQtComponents/Components/SearchLineEdit.h>
|
||||
#include <GemCatalog/GemCatalogHeaderWidget.h>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <TagWidget.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent)
|
||||
CartOverlayWidget::CartOverlayWidget(GemModel* gemModel, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_gemModel(gemModel)
|
||||
{
|
||||
setObjectName("GemCatalogCart");
|
||||
|
||||
m_layout = new QVBoxLayout();
|
||||
m_layout->setSpacing(0);
|
||||
m_layout->setMargin(0);
|
||||
m_layout->setAlignment(Qt::AlignTop);
|
||||
setLayout(m_layout);
|
||||
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
|
||||
QPushButton* closeButton = new QPushButton();
|
||||
closeButton->setFlat(true);
|
||||
closeButton->setFocusPolicy(Qt::NoFocus);
|
||||
closeButton->setIcon(QIcon(":/WindowClose.svg"));
|
||||
connect(closeButton, &QPushButton::clicked, this, [=]
|
||||
{
|
||||
deleteLater();
|
||||
});
|
||||
hLayout->addSpacerItem(new QSpacerItem(10, 0, QSizePolicy::Expanding));
|
||||
hLayout->addWidget(closeButton);
|
||||
m_layout->addLayout(hLayout);
|
||||
|
||||
// enabled
|
||||
{
|
||||
m_enabledWidget = new QWidget();
|
||||
m_enabledWidget->setFixedWidth(s_width);
|
||||
m_layout->addWidget(m_enabledWidget);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
m_enabledWidget->setLayout(layout);
|
||||
|
||||
m_enabledLabel = new QLabel();
|
||||
m_enabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel");
|
||||
layout->addWidget(m_enabledLabel);
|
||||
m_enabledTagContainer = new TagContainerWidget();
|
||||
layout->addWidget(m_enabledTagContainer);
|
||||
}
|
||||
|
||||
// disabled
|
||||
{
|
||||
m_disabledWidget = new QWidget();
|
||||
m_disabledWidget->setFixedWidth(s_width);
|
||||
m_layout->addWidget(m_disabledWidget);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setAlignment(Qt::AlignTop);
|
||||
m_disabledWidget->setLayout(layout);
|
||||
|
||||
m_disabledLabel = new QLabel();
|
||||
m_disabledLabel->setObjectName("GemCatalogCartOverlaySectionLabel");
|
||||
layout->addWidget(m_disabledLabel);
|
||||
m_disabledTagContainer = new TagContainerWidget();
|
||||
layout->addWidget(m_disabledTagContainer);
|
||||
}
|
||||
|
||||
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);
|
||||
|
||||
Update();
|
||||
connect(gemModel, &GemModel::dataChanged, this, [=]
|
||||
{
|
||||
Update();
|
||||
});
|
||||
}
|
||||
|
||||
void CartOverlayWidget::Update()
|
||||
{
|
||||
const QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
|
||||
if (toBeAdded.isEmpty())
|
||||
{
|
||||
m_enabledWidget->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_enabledTagContainer->Update(ConvertFromModelIndices(toBeAdded));
|
||||
m_enabledLabel->setText(QString("%1 %2").arg(QString::number(toBeAdded.size()), tr("Gems to be enabled")));
|
||||
m_enabledWidget->show();
|
||||
}
|
||||
|
||||
const QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
|
||||
if (toBeRemoved.isEmpty())
|
||||
{
|
||||
m_disabledWidget->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_disabledTagContainer->Update(ConvertFromModelIndices(toBeRemoved));
|
||||
m_disabledLabel->setText(QString("%1 %2").arg(QString::number(toBeRemoved.size()), tr("Gems to be disabled")));
|
||||
m_disabledWidget->show();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList CartOverlayWidget::ConvertFromModelIndices(const QVector<QModelIndex>& gems) const
|
||||
{
|
||||
QStringList gemNames;
|
||||
gemNames.reserve(gems.size());
|
||||
for (const QModelIndex& modelIndex : gems)
|
||||
{
|
||||
gemNames.push_back(GemModel::GetName(modelIndex));
|
||||
}
|
||||
return gemNames;
|
||||
}
|
||||
|
||||
CartButton::CartButton(GemModel* gemModel, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_gemModel(gemModel)
|
||||
{
|
||||
m_layout = new QHBoxLayout();
|
||||
m_layout->setMargin(0);
|
||||
setLayout(m_layout);
|
||||
|
||||
QPushButton* iconButton = new QPushButton();
|
||||
iconButton->setFlat(true);
|
||||
iconButton->setFocusPolicy(Qt::NoFocus);
|
||||
iconButton->setIcon(QIcon(":/Summary.svg"));
|
||||
iconButton->setFixedSize(s_iconSize, s_iconSize);
|
||||
connect(iconButton, &QPushButton::clicked, this, &CartButton::ShowOverlay);
|
||||
m_layout->addWidget(iconButton);
|
||||
|
||||
m_countLabel = new QLabel();
|
||||
m_countLabel->setObjectName("GemCatalogCartCountLabel");
|
||||
m_countLabel->setFixedHeight(s_iconSize - 1); // Compensate for the empty icon space by using a slightly smaller label height.
|
||||
m_layout->addWidget(m_countLabel);
|
||||
|
||||
m_dropDownButton = new QPushButton();
|
||||
m_dropDownButton->setFlat(true);
|
||||
m_dropDownButton->setFocusPolicy(Qt::NoFocus);
|
||||
m_dropDownButton->setIcon(QIcon(":/CarrotArrowDown.svg"));
|
||||
m_dropDownButton->setFixedSize(s_arrowDownIconSize, s_arrowDownIconSize);
|
||||
connect(m_dropDownButton, &QPushButton::clicked, this, &CartButton::ShowOverlay);
|
||||
m_layout->addWidget(m_dropDownButton);
|
||||
|
||||
// Adjust the label text whenever the model gets updated.
|
||||
connect(gemModel, &GemModel::dataChanged, [=]
|
||||
{
|
||||
const QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
|
||||
const QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
|
||||
|
||||
const int count = toBeAdded.size() + toBeRemoved.size();
|
||||
m_countLabel->setText(QString::number(count));
|
||||
|
||||
m_dropDownButton->setVisible(!toBeAdded.isEmpty() || !toBeRemoved.isEmpty());
|
||||
|
||||
// Automatically close the overlay window in case there are no gems to be enabled or disabled anymore.
|
||||
if (m_cartOverlay && toBeAdded.isEmpty() && toBeRemoved.isEmpty())
|
||||
{
|
||||
m_cartOverlay->deleteLater();
|
||||
m_cartOverlay = nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CartButton::mousePressEvent([[maybe_unused]] QMouseEvent* event)
|
||||
{
|
||||
ShowOverlay();
|
||||
}
|
||||
|
||||
void CartButton::ShowOverlay()
|
||||
{
|
||||
const QVector<QModelIndex> toBeAdded = m_gemModel->GatherGemsToBeAdded();
|
||||
const QVector<QModelIndex> toBeRemoved = m_gemModel->GatherGemsToBeRemoved();
|
||||
if (toBeAdded.isEmpty() && toBeRemoved.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_cartOverlay)
|
||||
{
|
||||
// Directly delete the former overlay before creating the new one.
|
||||
// Don't use deleteLater() here. This might overwrite the new overlay pointer
|
||||
// depending on the event queue.
|
||||
delete m_cartOverlay;
|
||||
}
|
||||
|
||||
m_cartOverlay = new CartOverlayWidget(m_gemModel, this);
|
||||
connect(m_cartOverlay, &QWidget::destroyed, this, [=]
|
||||
{
|
||||
// Reset the overlay pointer on destruction to prevent dangling pointers.
|
||||
m_cartOverlay = nullptr;
|
||||
});
|
||||
m_cartOverlay->show();
|
||||
|
||||
const QPoint parentPos = m_dropDownButton->mapToParent(m_dropDownButton->pos());
|
||||
const QPoint globalPos = m_dropDownButton->mapToGlobal(m_dropDownButton->pos());
|
||||
const QPoint offset(-4, 10);
|
||||
m_cartOverlay->setGeometry(globalPos.x() - parentPos.x() - m_cartOverlay->width() + width() + offset.x(),
|
||||
globalPos.y() + offset.y(),
|
||||
m_cartOverlay->width(),
|
||||
m_cartOverlay->height());
|
||||
}
|
||||
|
||||
CartButton::~CartButton()
|
||||
{
|
||||
// Make sure the overlay window is automatically closed in case the gem catalog is destroyed.
|
||||
if (m_cartOverlay)
|
||||
{
|
||||
m_cartOverlay->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
GemCatalogHeaderWidget::GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent)
|
||||
: QFrame(parent)
|
||||
{
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setAlignment(Qt::AlignLeft);
|
||||
hLayout->setMargin(0);
|
||||
hLayout->setContentsMargins(10, 7, 10, 7);
|
||||
setLayout(hLayout);
|
||||
|
||||
setObjectName("GemCatalogHeaderWidget");
|
||||
|
||||
hLayout->addSpacing(7);
|
||||
setFixedHeight(s_height);
|
||||
|
||||
QLabel* titleLabel = new QLabel(tr("Gem Catalog"));
|
||||
titleLabel->setObjectName("GemCatalogTitle");
|
||||
@@ -35,17 +240,23 @@ namespace O3DE::ProjectManager
|
||||
|
||||
hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
|
||||
|
||||
AzQtComponents::SearchLineEdit* filterLineEdit = new AzQtComponents::SearchLineEdit();
|
||||
filterLineEdit->setStyleSheet("background-color: #DDDDDD;");
|
||||
connect(filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text)
|
||||
m_filterLineEdit = new AzQtComponents::SearchLineEdit();
|
||||
m_filterLineEdit->setStyleSheet("background-color: #DDDDDD;");
|
||||
connect(m_filterLineEdit, &QLineEdit::textChanged, this, [=](const QString& text)
|
||||
{
|
||||
filterProxyModel->SetSearchString(text);
|
||||
});
|
||||
hLayout->addWidget(filterLineEdit);
|
||||
hLayout->addWidget(m_filterLineEdit);
|
||||
|
||||
hLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
|
||||
hLayout->addSpacerItem(new QSpacerItem(140, 0, QSizePolicy::Fixed));
|
||||
|
||||
setFixedHeight(60);
|
||||
hLayout->addSpacerItem(new QSpacerItem(75, 0, QSizePolicy::Fixed));
|
||||
|
||||
CartButton* cartButton = new CartButton(gemModel);
|
||||
hLayout->addWidget(cartButton);
|
||||
}
|
||||
|
||||
void GemCatalogHeaderWidget::ReinitForProject()
|
||||
{
|
||||
m_filterLineEdit->setText({});
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -13,19 +13,81 @@
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <AzQtComponents/Components/SearchLineEdit.h>
|
||||
#include <GemCatalog/GemModel.h>
|
||||
#include <GemCatalog/GemSortFilterProxyModel.h>
|
||||
#include <TagWidget.h>
|
||||
#include <QFrame>
|
||||
#include <QLabel>
|
||||
#include <QDialog>
|
||||
#include <QMoveEvent>
|
||||
#include <QVBoxLayout>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class CartOverlayWidget
|
||||
: public QWidget
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
CartOverlayWidget(GemModel* gemModel, QWidget* parent = nullptr);
|
||||
void Update();
|
||||
|
||||
private:
|
||||
QStringList ConvertFromModelIndices(const QVector<QModelIndex>& gems) const;
|
||||
|
||||
QVBoxLayout* m_layout = nullptr;
|
||||
GemModel* m_gemModel = nullptr;
|
||||
|
||||
QWidget* m_enabledWidget = nullptr;
|
||||
QLabel* m_enabledLabel = nullptr;
|
||||
TagContainerWidget* m_enabledTagContainer = nullptr;
|
||||
|
||||
QWidget* m_disabledWidget = nullptr;
|
||||
QLabel* m_disabledLabel = nullptr;
|
||||
TagContainerWidget* m_disabledTagContainer = nullptr;
|
||||
|
||||
inline constexpr static int s_width = 240;
|
||||
};
|
||||
|
||||
class CartButton
|
||||
: public QWidget
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
CartButton(GemModel* gemModel, QWidget* parent = nullptr);
|
||||
~CartButton();
|
||||
void ShowOverlay();
|
||||
|
||||
private:
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
GemModel* m_gemModel = nullptr;
|
||||
QHBoxLayout* m_layout = nullptr;
|
||||
QLabel* m_countLabel = nullptr;
|
||||
QPushButton* m_dropDownButton = nullptr;
|
||||
CartOverlayWidget* m_cartOverlay = nullptr;
|
||||
|
||||
inline constexpr static int s_iconSize = 24;
|
||||
inline constexpr static int s_arrowDownIconSize = 8;
|
||||
};
|
||||
|
||||
class GemCatalogHeaderWidget
|
||||
: public QFrame
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit GemCatalogHeaderWidget(GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr);
|
||||
explicit GemCatalogHeaderWidget(GemModel* gemModel, GemSortFilterProxyModel* filterProxyModel, QWidget* parent = nullptr);
|
||||
~GemCatalogHeaderWidget() = default;
|
||||
|
||||
void ReinitForProject();
|
||||
|
||||
private:
|
||||
AzQtComponents::SearchLineEdit* m_filterLineEdit = nullptr;
|
||||
inline constexpr static int s_height = 60;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include <GemCatalog/GemCatalogScreen.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <GemCatalog/GemCatalogHeaderWidget.h>
|
||||
#include <GemCatalog/GemListHeaderWidget.h>
|
||||
#include <GemCatalog/GemSortFilterProxyModel.h>
|
||||
#include <QVBoxLayout>
|
||||
@@ -35,8 +34,8 @@ namespace O3DE::ProjectManager
|
||||
vLayout->setSpacing(0);
|
||||
setLayout(vLayout);
|
||||
|
||||
GemCatalogHeaderWidget* headerWidget = new GemCatalogHeaderWidget(m_proxModel);
|
||||
vLayout->addWidget(headerWidget);
|
||||
m_headerWidget = new GemCatalogHeaderWidget(m_gemModel, m_proxModel);
|
||||
vLayout->addWidget(m_headerWidget);
|
||||
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setMargin(0);
|
||||
@@ -77,10 +76,11 @@ namespace O3DE::ProjectManager
|
||||
m_filterWidget->deleteLater();
|
||||
}
|
||||
|
||||
m_proxModel->ResetFilters();
|
||||
m_filterWidget = new GemFilterWidget(m_proxModel);
|
||||
m_filterWidgetLayout->addWidget(m_filterWidget);
|
||||
|
||||
m_proxModel->InvalidateFilter();
|
||||
m_headerWidget->ReinitForProject();
|
||||
|
||||
// Select the first entry after everything got correctly sized
|
||||
QTimer::singleShot(200, [=]{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ScreenWidget.h>
|
||||
#include <GemCatalog/GemCatalogHeaderWidget.h>
|
||||
#include <GemCatalog/GemFilterWidget.h>
|
||||
#include <GemCatalog/GemListView.h>
|
||||
#include <GemCatalog/GemInspector.h>
|
||||
@@ -40,6 +41,7 @@ namespace O3DE::ProjectManager
|
||||
GemListView* m_gemListView = nullptr;
|
||||
GemInspector* m_gemInspector = nullptr;
|
||||
GemModel* m_gemModel = nullptr;
|
||||
GemCatalogHeaderWidget* m_headerWidget = nullptr;
|
||||
GemSortFilterProxyModel* m_proxModel = nullptr;
|
||||
QVBoxLayout* m_filterWidgetLayout = nullptr;
|
||||
GemFilterWidget* m_filterWidget = nullptr;
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
QFont standardFont(options.font);
|
||||
standardFont.setPixelSize(s_fontSize);
|
||||
QFontMetrics standardFontMetrics(standardFont);
|
||||
|
||||
painter->save();
|
||||
painter->setClipping(true);
|
||||
@@ -78,8 +79,10 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
|
||||
// Gem name
|
||||
const QString gemName = GemModel::GetName(modelIndex);
|
||||
QString gemName = GemModel::GetName(modelIndex);
|
||||
QFont gemNameFont(options.font);
|
||||
const int firstColumnMaxTextWidth = s_summaryStartX - 30;
|
||||
gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth);
|
||||
gemNameFont.setPixelSize(s_gemNameFontSize);
|
||||
gemNameFont.setBold(true);
|
||||
QRect gemNameRect = GetTextRect(gemNameFont, gemName, s_gemNameFontSize);
|
||||
@@ -90,7 +93,8 @@ namespace O3DE::ProjectManager
|
||||
painter->drawText(gemNameRect, Qt::TextSingleLine, gemName);
|
||||
|
||||
// Gem creator
|
||||
const QString gemCreator = GemModel::GetCreator(modelIndex);
|
||||
QString gemCreator = GemModel::GetCreator(modelIndex);
|
||||
gemCreator = standardFontMetrics.elidedText(gemCreator, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth);
|
||||
QRect gemCreatorRect = GetTextRect(standardFont, gemCreator, s_fontSize);
|
||||
gemCreatorRect.moveTo(contentRect.left(), contentRect.top() + gemNameRect.height());
|
||||
|
||||
|
||||
@@ -130,4 +130,15 @@ namespace O3DE::ProjectManager
|
||||
invalidate();
|
||||
emit OnInvalidated();
|
||||
}
|
||||
|
||||
void GemSortFilterProxyModel::ResetFilters()
|
||||
{
|
||||
m_searchString.clear();
|
||||
m_gemOriginFilter = {};
|
||||
m_platformFilter = {};
|
||||
m_typeFilter = {};
|
||||
m_featureFilter = {};
|
||||
|
||||
InvalidateFilter();
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace O3DE::ProjectManager
|
||||
void SetFeatures(const QSet<QString>& features) { m_featureFilter = features; InvalidateFilter(); }
|
||||
|
||||
void InvalidateFilter();
|
||||
void ResetFilters();
|
||||
|
||||
signals:
|
||||
void OnInvalidated();
|
||||
|
||||
@@ -14,8 +14,12 @@
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <TemplateButtonWidget.h>
|
||||
#include <PathValidator.h>
|
||||
#include <EngineInfo.h>
|
||||
#include <CreateProjectCtrl.h>
|
||||
#include <TagWidget.h>
|
||||
#include <AzQtComponents/Components/FlowLayout.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
@@ -28,10 +32,12 @@
|
||||
#include <QSpacerItem>
|
||||
#include <QStandardPaths>
|
||||
#include <QFrame>
|
||||
#include <QScrollArea>
|
||||
#include <QAbstractButton>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
constexpr const char* k_pathProperty = "Path";
|
||||
constexpr const char* k_templateIndexProperty = "TemplateIndex";
|
||||
|
||||
NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent)
|
||||
: ProjectSettingsScreen(parent)
|
||||
@@ -59,30 +65,69 @@ namespace O3DE::ProjectManager
|
||||
projectTemplateDetailsLabel->setObjectName("projectTemplateDetailsLabel");
|
||||
containerLayout->addWidget(projectTemplateDetailsLabel);
|
||||
|
||||
QHBoxLayout* templateLayout = new QHBoxLayout(this);
|
||||
containerLayout->addItem(templateLayout);
|
||||
|
||||
// we might have enough templates that we need to scroll
|
||||
QScrollArea* templatesScrollArea = new QScrollArea(this);
|
||||
QWidget* scrollWidget = new QWidget();
|
||||
|
||||
FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize);
|
||||
scrollWidget->setLayout(flowLayout);
|
||||
|
||||
templatesScrollArea->setWidget(scrollWidget);
|
||||
templatesScrollArea->setWidgetResizable(true);
|
||||
|
||||
m_projectTemplateButtonGroup = new QButtonGroup(this);
|
||||
m_projectTemplateButtonGroup->setObjectName("templateButtonGroup");
|
||||
|
||||
// QButtonGroup has overloaded buttonClicked methods so we need the QOverload
|
||||
connect(
|
||||
m_projectTemplateButtonGroup, QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked), this,
|
||||
[=](QAbstractButton* button)
|
||||
{
|
||||
if (button && button->property(k_templateIndexProperty).isValid())
|
||||
{
|
||||
int projectIndex = button->property(k_templateIndexProperty).toInt();
|
||||
UpdateTemplateDetails(m_templates.at(projectIndex));
|
||||
}
|
||||
});
|
||||
|
||||
auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates();
|
||||
if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty())
|
||||
{
|
||||
for (const ProjectTemplateInfo& projectTemplate : templatesResult.GetValue())
|
||||
{
|
||||
QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this);
|
||||
radioButton->setProperty(k_pathProperty, projectTemplate.m_path);
|
||||
m_projectTemplateButtonGroup->addButton(radioButton);
|
||||
m_templates = templatesResult.GetValue();
|
||||
|
||||
containerLayout->addWidget(radioButton);
|
||||
// sort alphabetically by display name because they could be in any order
|
||||
std::sort(m_templates.begin(), m_templates.end(), [](const ProjectTemplateInfo& arg1, const ProjectTemplateInfo& arg2)
|
||||
{
|
||||
return arg1.m_displayName.toLower() < arg2.m_displayName.toLower();
|
||||
});
|
||||
|
||||
for (int index = 0; index < m_templates.size(); ++index)
|
||||
{
|
||||
ProjectTemplateInfo projectTemplate = m_templates.at(index);
|
||||
QString projectPreviewPath = projectTemplate.m_path + "/Template/preview.png";
|
||||
QFileInfo doesPreviewExist(projectPreviewPath);
|
||||
if (!doesPreviewExist.exists() || !doesPreviewExist.isFile())
|
||||
{
|
||||
projectPreviewPath = ":/DefaultTemplate.png";
|
||||
}
|
||||
TemplateButton* templateButton = new TemplateButton(projectPreviewPath, projectTemplate.m_displayName, this);
|
||||
templateButton->setCheckable(true);
|
||||
templateButton->setProperty(k_templateIndexProperty, index);
|
||||
|
||||
m_projectTemplateButtonGroup->addButton(templateButton);
|
||||
|
||||
flowLayout->addWidget(templateButton);
|
||||
}
|
||||
|
||||
m_projectTemplateButtonGroup->buttons().first()->setChecked(true);
|
||||
}
|
||||
containerLayout->addWidget(templatesScrollArea);
|
||||
}
|
||||
projectTemplateWidget->setLayout(containerLayout);
|
||||
m_verticalLayout->addWidget(projectTemplateWidget);
|
||||
|
||||
QWidget* projectTemplateDetails = new QWidget(this);
|
||||
QFrame* projectTemplateDetails = CreateTemplateDetails(s_templateDetailsContentMargin);
|
||||
projectTemplateDetails->setObjectName("projectTemplateDetails");
|
||||
m_horizontalLayout->addWidget(projectTemplateDetails);
|
||||
}
|
||||
@@ -109,11 +154,71 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void NewProjectSettingsScreen::NotifyCurrentScreen()
|
||||
{
|
||||
if (!m_templates.isEmpty())
|
||||
{
|
||||
UpdateTemplateDetails(m_templates.first());
|
||||
}
|
||||
|
||||
Validate();
|
||||
}
|
||||
|
||||
QString NewProjectSettingsScreen::GetProjectTemplatePath()
|
||||
{
|
||||
return m_projectTemplateButtonGroup->checkedButton()->property(k_pathProperty).toString();
|
||||
const int templateIndex = m_projectTemplateButtonGroup->checkedButton()->property(k_templateIndexProperty).toInt();
|
||||
return m_templates.at(templateIndex).m_path;
|
||||
}
|
||||
|
||||
QFrame* NewProjectSettingsScreen::CreateTemplateDetails(int margin)
|
||||
{
|
||||
QFrame* projectTemplateDetails = new QFrame(this);
|
||||
projectTemplateDetails->setObjectName("projectTemplateDetails");
|
||||
QVBoxLayout* templateDetailsLayout = new QVBoxLayout();
|
||||
templateDetailsLayout->setContentsMargins(margin, margin, margin, margin);
|
||||
templateDetailsLayout->setAlignment(Qt::AlignTop);
|
||||
{
|
||||
m_templateDisplayName = new QLabel(this);
|
||||
m_templateDisplayName->setObjectName("displayName");
|
||||
templateDetailsLayout->addWidget(m_templateDisplayName);
|
||||
|
||||
m_templateSummary = new QLabel(this);
|
||||
m_templateSummary->setObjectName("summary");
|
||||
m_templateSummary->setWordWrap(true);
|
||||
templateDetailsLayout->addWidget(m_templateSummary);
|
||||
|
||||
QLabel* includedGemsTitle = new QLabel(tr("Included Gems"), this);
|
||||
includedGemsTitle->setObjectName("includedGemsTitle");
|
||||
templateDetailsLayout->addWidget(includedGemsTitle);
|
||||
|
||||
m_templateIncludedGems = new TagContainerWidget(this);
|
||||
m_templateIncludedGems->setObjectName("includedGems");
|
||||
templateDetailsLayout->addWidget(m_templateIncludedGems);
|
||||
|
||||
#ifdef TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
QLabel* moreGemsLabel = new QLabel(tr("Looking for more Gems?"), this);
|
||||
moreGemsLabel->setObjectName("moreGems");
|
||||
templateDetailsLayout->addWidget(moreGemsLabel);
|
||||
|
||||
QLabel* browseCatalogLabel = new QLabel(tr("Browse the Gems Catalog to further customize your project."), this);
|
||||
browseCatalogLabel->setObjectName("browseCatalog");
|
||||
browseCatalogLabel->setWordWrap(true);
|
||||
templateDetailsLayout->addWidget(browseCatalogLabel);
|
||||
|
||||
QPushButton* configureGemsButton = new QPushButton(tr("Configure with more Gems"), this);
|
||||
connect(configureGemsButton, &QPushButton::clicked, this, [=]()
|
||||
{
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog);
|
||||
});
|
||||
templateDetailsLayout->addWidget(configureGemsButton);
|
||||
#endif // TEMPLATE_GEM_CONFIGURATION_ENABLED
|
||||
}
|
||||
projectTemplateDetails->setLayout(templateDetailsLayout);
|
||||
return projectTemplateDetails;
|
||||
}
|
||||
|
||||
void NewProjectSettingsScreen::UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo)
|
||||
{
|
||||
m_templateDisplayName->setText(templateInfo.m_displayName);
|
||||
m_templateSummary->setText(templateInfo.m_summary);
|
||||
m_templateIncludedGems->Update(templateInfo.m_includedGems);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -13,12 +13,17 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ProjectSettingsScreen.h>
|
||||
#include <ProjectTemplateInfo.h>
|
||||
#include <QVector>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QButtonGroup)
|
||||
QT_FORWARD_DECLARE_CLASS(QLabel)
|
||||
QT_FORWARD_DECLARE_CLASS(QFrame)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
QT_FORWARD_DECLARE_CLASS(TagContainerWidget)
|
||||
class NewProjectSettingsScreen
|
||||
: public ProjectSettingsScreen
|
||||
{
|
||||
@@ -33,8 +38,17 @@ namespace O3DE::ProjectManager
|
||||
|
||||
private:
|
||||
QString GetDefaultProjectPath();
|
||||
QFrame* CreateTemplateDetails(int margin);
|
||||
void UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo);
|
||||
|
||||
QButtonGroup* m_projectTemplateButtonGroup;
|
||||
QLabel* m_templateDisplayName;
|
||||
QLabel* m_templateSummary;
|
||||
TagContainerWidget* m_templateIncludedGems;
|
||||
QVector<ProjectTemplateInfo> m_templates;
|
||||
|
||||
inline constexpr static int s_spacerSize = 20;
|
||||
inline constexpr static int s_templateDetailsContentMargin = 20;
|
||||
};
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ProjectBuilder.h>
|
||||
#include <ProjectButtonWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
|
||||
#include <QProcess>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QMessageBox>
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
|
||||
//#define MOCK_BUILD_PROJECT true
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
// 10 Minutes
|
||||
constexpr int MaxBuildTimeMSecs = 600000;
|
||||
static const QString BuildPathPostfix = "windows_vs2019";
|
||||
static const QString ErrorLogPathPostfix = "CMakeFiles/CMakeProjectBuildError.log";
|
||||
|
||||
ProjectBuilderWorker::ProjectBuilderWorker(const ProjectInfo& projectInfo)
|
||||
: QObject()
|
||||
, m_projectInfo(projectInfo)
|
||||
{
|
||||
}
|
||||
|
||||
void ProjectBuilderWorker::BuildProject()
|
||||
{
|
||||
#ifdef MOCK_BUILD_PROJECT
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
QThread::sleep(1);
|
||||
UpdateProgress(i * 10);
|
||||
}
|
||||
Done(m_projectPath);
|
||||
#else
|
||||
EngineInfo engineInfo;
|
||||
|
||||
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
|
||||
if (engineInfoResult.IsSuccess())
|
||||
{
|
||||
engineInfo = engineInfoResult.GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
emit Done(tr("Failed to get engine info."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Show some kind of progress with very approximate estimates
|
||||
UpdateProgress(1);
|
||||
|
||||
QProcessEnvironment currentEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
// Append cmake path to PATH incase it is missing
|
||||
QDir cmakePath(engineInfo.m_path);
|
||||
cmakePath.cd("cmake/runtime/bin");
|
||||
QString pathValue = currentEnvironment.value("PATH");
|
||||
pathValue += ";" + cmakePath.path();
|
||||
currentEnvironment.insert("PATH", pathValue);
|
||||
|
||||
QProcess configProjectProcess;
|
||||
configProjectProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
configProjectProcess.setWorkingDirectory(m_projectInfo.m_path);
|
||||
configProjectProcess.setProcessEnvironment(currentEnvironment);
|
||||
|
||||
configProjectProcess.start(
|
||||
"cmake",
|
||||
QStringList
|
||||
{
|
||||
"-B",
|
||||
QDir(m_projectInfo.m_path).filePath(BuildPathPostfix),
|
||||
"-S",
|
||||
m_projectInfo.m_path,
|
||||
"-G",
|
||||
"Visual Studio 16",
|
||||
"-DLY_3RDPARTY_PATH=" + engineInfo.m_thirdPartyPath
|
||||
});
|
||||
|
||||
if (!configProjectProcess.waitForStarted())
|
||||
{
|
||||
emit Done(tr("Configuring project failed to start."));
|
||||
return;
|
||||
}
|
||||
if (!configProjectProcess.waitForFinished(MaxBuildTimeMSecs))
|
||||
{
|
||||
WriteErrorLog(configProjectProcess.readAllStandardOutput());
|
||||
emit Done(tr("Configuring project timed out. See log for details"));
|
||||
return;
|
||||
}
|
||||
|
||||
QString configProjectOutput(configProjectProcess.readAllStandardOutput());
|
||||
if (configProjectProcess.exitCode() != 0 || !configProjectOutput.contains("Generating done"))
|
||||
{
|
||||
WriteErrorLog(configProjectOutput);
|
||||
emit Done(tr("Configuring project failed. See log for details."));
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateProgress(20);
|
||||
|
||||
QProcess buildProjectProcess;
|
||||
buildProjectProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
buildProjectProcess.setWorkingDirectory(m_projectInfo.m_path);
|
||||
buildProjectProcess.setProcessEnvironment(currentEnvironment);
|
||||
|
||||
buildProjectProcess.start(
|
||||
"cmake",
|
||||
QStringList
|
||||
{
|
||||
"--build",
|
||||
QDir(m_projectInfo.m_path).filePath(BuildPathPostfix),
|
||||
"--target",
|
||||
m_projectInfo.m_projectName + ".GameLauncher",
|
||||
"Editor",
|
||||
"--config",
|
||||
"profile"
|
||||
});
|
||||
|
||||
if (!buildProjectProcess.waitForStarted())
|
||||
{
|
||||
emit Done(tr("Building project failed to start."));
|
||||
return;
|
||||
}
|
||||
if (!buildProjectProcess.waitForFinished(MaxBuildTimeMSecs))
|
||||
{
|
||||
WriteErrorLog(configProjectProcess.readAllStandardOutput());
|
||||
emit Done(tr("Building project timed out. See log for details"));
|
||||
return;
|
||||
}
|
||||
|
||||
QString buildProjectOutput(buildProjectProcess.readAllStandardOutput());
|
||||
if (configProjectProcess.exitCode() != 0)
|
||||
{
|
||||
WriteErrorLog(buildProjectOutput);
|
||||
emit Done(tr("Building project failed. See log for details."));
|
||||
}
|
||||
else
|
||||
{
|
||||
emit Done("");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QString ProjectBuilderWorker::LogFilePath() const
|
||||
{
|
||||
QDir logFilePath(m_projectInfo.m_path);
|
||||
logFilePath.cd(BuildPathPostfix);
|
||||
return logFilePath.filePath(ErrorLogPathPostfix);
|
||||
}
|
||||
|
||||
void ProjectBuilderWorker::WriteErrorLog(const QString& log)
|
||||
{
|
||||
QFile logFile(LogFilePath());
|
||||
// Overwrite file with truncate
|
||||
if (logFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
|
||||
{
|
||||
QTextStream output(&logFile);
|
||||
output << log;
|
||||
logFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
ProjectBuilderController::ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent)
|
||||
: QObject()
|
||||
, m_projectInfo(projectInfo)
|
||||
, m_projectButton(projectButton)
|
||||
, m_parent(parent)
|
||||
{
|
||||
m_worker = new ProjectBuilderWorker(m_projectInfo);
|
||||
m_worker->moveToThread(&m_workerThread);
|
||||
|
||||
connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater);
|
||||
connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject);
|
||||
connect(m_worker, &ProjectBuilderWorker::Done, this, &ProjectBuilderController::HandleResults);
|
||||
connect(m_worker, &ProjectBuilderWorker::UpdateProgress, this, &ProjectBuilderController::UpdateUIProgress);
|
||||
}
|
||||
|
||||
ProjectBuilderController::~ProjectBuilderController()
|
||||
{
|
||||
m_workerThread.quit();
|
||||
m_workerThread.wait();
|
||||
}
|
||||
|
||||
void ProjectBuilderController::Start()
|
||||
{
|
||||
m_workerThread.start();
|
||||
UpdateUIProgress(0);
|
||||
}
|
||||
|
||||
void ProjectBuilderController::SetProjectButton(ProjectButton* projectButton)
|
||||
{
|
||||
m_projectButton = projectButton;
|
||||
}
|
||||
|
||||
QString ProjectBuilderController::GetProjectPath() const
|
||||
{
|
||||
return m_projectInfo.m_path;
|
||||
}
|
||||
|
||||
void ProjectBuilderController::UpdateUIProgress(int progress)
|
||||
{
|
||||
if (m_projectButton)
|
||||
{
|
||||
m_projectButton->SetButtonOverlayText(QString("%1 (%2%)\n\n").arg(tr("Building Project..."), QString::number(progress)));
|
||||
m_projectButton->SetProgressBarValue(progress);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBuilderController::HandleResults(const QString& result)
|
||||
{
|
||||
if (!result.isEmpty())
|
||||
{
|
||||
if (result.contains(tr("log")))
|
||||
{
|
||||
QMessageBox::StandardButton openLog = QMessageBox::critical(
|
||||
m_parent,
|
||||
tr("Project Failed to Build!"),
|
||||
result + tr("\n\nWould you like to view log?"),
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (openLog == QMessageBox::Yes)
|
||||
{
|
||||
// Open application assigned to this file type
|
||||
QDesktopServices::openUrl(QUrl("file:///" + m_worker->LogFilePath()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(m_parent, tr("Project Failed to Build!"), result);
|
||||
}
|
||||
}
|
||||
|
||||
emit Done();
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ProjectInfo.h>
|
||||
|
||||
#include <QThread>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
QT_FORWARD_DECLARE_CLASS(ProjectButton)
|
||||
|
||||
class ProjectBuilderWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ProjectBuilderWorker(const ProjectInfo& projectInfo);
|
||||
~ProjectBuilderWorker() = default;
|
||||
|
||||
QString LogFilePath() const;
|
||||
|
||||
public slots:
|
||||
void BuildProject();
|
||||
|
||||
signals:
|
||||
void UpdateProgress(int progress);
|
||||
void Done(QString result);
|
||||
|
||||
private:
|
||||
void WriteErrorLog(const QString& log);
|
||||
|
||||
ProjectInfo m_projectInfo;
|
||||
};
|
||||
|
||||
class ProjectBuilderController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ProjectBuilderController(const ProjectInfo& projectInfo, ProjectButton* projectButton, QWidget* parent = nullptr);
|
||||
~ProjectBuilderController();
|
||||
|
||||
void SetProjectButton(ProjectButton* projectButton);
|
||||
QString GetProjectPath() const;
|
||||
|
||||
public slots:
|
||||
void Start();
|
||||
void UpdateUIProgress(int progress);
|
||||
void HandleResults(const QString& result);
|
||||
|
||||
signals:
|
||||
void Done();
|
||||
|
||||
private:
|
||||
ProjectInfo m_projectInfo;
|
||||
ProjectBuilderWorker* m_worker;
|
||||
QThread m_workerThread;
|
||||
ProjectButton* m_projectButton;
|
||||
QWidget* m_parent;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <QPixmap>
|
||||
#include <QMenu>
|
||||
#include <QSpacerItem>
|
||||
#include <QProgressBar>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -31,11 +32,26 @@ namespace O3DE::ProjectManager
|
||||
: QLabel(parent)
|
||||
{
|
||||
setObjectName("labelButton");
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout(this);
|
||||
vLayout->setContentsMargins(0, 0, 0, 0);
|
||||
vLayout->setSpacing(5);
|
||||
|
||||
setLayout(vLayout);
|
||||
m_overlayLabel = new QLabel("", this);
|
||||
m_overlayLabel->setObjectName("labelButtonOverlay");
|
||||
m_overlayLabel->setWordWrap(true);
|
||||
m_overlayLabel->setAlignment(Qt::AlignCenter);
|
||||
m_overlayLabel->setVisible(false);
|
||||
vLayout->addWidget(m_overlayLabel);
|
||||
|
||||
m_buildButton = new QPushButton(tr("Build Project"), this);
|
||||
m_buildButton->setVisible(false);
|
||||
|
||||
m_progressBar = new QProgressBar(this);
|
||||
m_progressBar->setObjectName("labelButtonProgressBar");
|
||||
m_progressBar->setVisible(false);
|
||||
vLayout->addWidget(m_progressBar);
|
||||
}
|
||||
|
||||
void LabelButton::mousePressEvent([[maybe_unused]] QMouseEvent* event)
|
||||
@@ -57,7 +73,22 @@ namespace O3DE::ProjectManager
|
||||
m_overlayLabel->setText(text);
|
||||
}
|
||||
|
||||
ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent)
|
||||
QLabel* LabelButton::GetOverlayLabel()
|
||||
{
|
||||
return m_overlayLabel;
|
||||
}
|
||||
|
||||
QProgressBar* LabelButton::GetProgressBar()
|
||||
{
|
||||
return m_progressBar;
|
||||
}
|
||||
|
||||
QPushButton* LabelButton::GetBuildButton()
|
||||
{
|
||||
return m_buildButton;
|
||||
}
|
||||
|
||||
ProjectButton::ProjectButton(const ProjectInfo& projectInfo, QWidget* parent, bool processing)
|
||||
: QFrame(parent)
|
||||
, m_projectInfo(projectInfo)
|
||||
{
|
||||
@@ -66,10 +97,18 @@ namespace O3DE::ProjectManager
|
||||
m_projectInfo.m_imagePath = ":/DefaultProjectImage.png";
|
||||
}
|
||||
|
||||
Setup();
|
||||
BaseSetup();
|
||||
if (processing)
|
||||
{
|
||||
ProcessingSetup();
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadySetup();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectButton::Setup()
|
||||
void ProjectButton::BaseSetup()
|
||||
{
|
||||
setObjectName("projectButton");
|
||||
|
||||
@@ -87,8 +126,37 @@ namespace O3DE::ProjectManager
|
||||
m_projectImageLabel->setPixmap(
|
||||
QPixmap(m_projectInfo.m_imagePath).scaled(m_projectImageLabel->size(), Qt::KeepAspectRatioByExpanding));
|
||||
|
||||
m_projectFooter = new QFrame(this);
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_projectFooter->setLayout(hLayout);
|
||||
{
|
||||
QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this);
|
||||
hLayout->addWidget(projectNameLabel);
|
||||
}
|
||||
|
||||
vLayout->addWidget(m_projectFooter);
|
||||
}
|
||||
|
||||
void ProjectButton::ProcessingSetup()
|
||||
{
|
||||
m_projectImageLabel->GetOverlayLabel()->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||
m_projectImageLabel->SetEnabled(false);
|
||||
m_projectImageLabel->SetOverlayText(tr("Processing...\n\n"));
|
||||
|
||||
QProgressBar* progressBar = m_projectImageLabel->GetProgressBar();
|
||||
progressBar->setVisible(true);
|
||||
progressBar->setValue(0);
|
||||
}
|
||||
|
||||
void ProjectButton::ReadySetup()
|
||||
{
|
||||
connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectInfo.m_path); });
|
||||
connect(m_projectImageLabel->GetBuildButton(), &QPushButton::clicked, [this](){ emit BuildProject(m_projectInfo); });
|
||||
|
||||
QMenu* menu = new QMenu(this);
|
||||
menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); });
|
||||
menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(tr("Open Project folder..."), this, [this]()
|
||||
{
|
||||
@@ -100,30 +168,33 @@ namespace O3DE::ProjectManager
|
||||
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
|
||||
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });
|
||||
|
||||
QFrame* footer = new QFrame(this);
|
||||
QHBoxLayout* hLayout = new QHBoxLayout();
|
||||
hLayout->setContentsMargins(0, 0, 0, 0);
|
||||
footer->setLayout(hLayout);
|
||||
{
|
||||
QLabel* projectNameLabel = new QLabel(m_projectInfo.m_displayName, this);
|
||||
hLayout->addWidget(projectNameLabel);
|
||||
|
||||
QPushButton* projectMenuButton = new QPushButton(this);
|
||||
projectMenuButton->setObjectName("projectMenuButton");
|
||||
projectMenuButton->setMenu(menu);
|
||||
hLayout->addWidget(projectMenuButton);
|
||||
}
|
||||
|
||||
vLayout->addWidget(footer);
|
||||
QPushButton* projectMenuButton = new QPushButton(this);
|
||||
projectMenuButton->setObjectName("projectMenuButton");
|
||||
projectMenuButton->setMenu(menu);
|
||||
m_projectFooter->layout()->addWidget(projectMenuButton);
|
||||
}
|
||||
|
||||
void ProjectButton::SetButtonEnabled(bool enabled)
|
||||
void ProjectButton::SetLaunchButtonEnabled(bool enabled)
|
||||
{
|
||||
m_projectImageLabel->SetEnabled(enabled);
|
||||
}
|
||||
|
||||
void ProjectButton::ShowBuildButton(bool show)
|
||||
{
|
||||
QSpacerItem* buttonSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
m_projectImageLabel->layout()->addItem(buttonSpacer);
|
||||
m_projectImageLabel->layout()->addWidget(m_projectImageLabel->GetBuildButton());
|
||||
m_projectImageLabel->GetBuildButton()->setVisible(show);
|
||||
}
|
||||
|
||||
void ProjectButton::SetButtonOverlayText(const QString& text)
|
||||
{
|
||||
m_projectImageLabel->SetOverlayText(text);
|
||||
}
|
||||
|
||||
void ProjectButton::SetProgressBarValue(int progress)
|
||||
{
|
||||
m_projectImageLabel->GetProgressBar()->setValue(progress);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
QT_FORWARD_DECLARE_CLASS(QPixmap)
|
||||
QT_FORWARD_DECLARE_CLASS(QPushButton)
|
||||
QT_FORWARD_DECLARE_CLASS(QAction)
|
||||
QT_FORWARD_DECLARE_CLASS(QProgressBar)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -36,6 +37,10 @@ namespace O3DE::ProjectManager
|
||||
void SetEnabled(bool enabled);
|
||||
void SetOverlayText(const QString& text);
|
||||
|
||||
QLabel* GetOverlayLabel();
|
||||
QProgressBar* GetProgressBar();
|
||||
QPushButton* GetBuildButton();
|
||||
|
||||
signals:
|
||||
void triggered();
|
||||
|
||||
@@ -44,6 +49,8 @@ namespace O3DE::ProjectManager
|
||||
|
||||
private:
|
||||
QLabel* m_overlayLabel;
|
||||
QProgressBar* m_progressBar;
|
||||
QPushButton* m_buildButton;
|
||||
bool m_enabled = true;
|
||||
};
|
||||
|
||||
@@ -53,11 +60,13 @@ namespace O3DE::ProjectManager
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr);
|
||||
explicit ProjectButton(const ProjectInfo& m_projectInfo, QWidget* parent = nullptr, bool processing = false);
|
||||
~ProjectButton() = default;
|
||||
|
||||
void SetButtonEnabled(bool enabled);
|
||||
void SetLaunchButtonEnabled(bool enabled);
|
||||
void ShowBuildButton(bool show);
|
||||
void SetButtonOverlayText(const QString& text);
|
||||
void SetProgressBarValue(int progress);
|
||||
|
||||
signals:
|
||||
void OpenProject(const QString& projectName);
|
||||
@@ -65,11 +74,15 @@ namespace O3DE::ProjectManager
|
||||
void CopyProject(const QString& projectName);
|
||||
void RemoveProject(const QString& projectName);
|
||||
void DeleteProject(const QString& projectName);
|
||||
void BuildProject(const ProjectInfo& projectInfo);
|
||||
|
||||
private:
|
||||
void Setup();
|
||||
void BaseSetup();
|
||||
void ProcessingSetup();
|
||||
void ReadySetup();
|
||||
|
||||
ProjectInfo m_projectInfo;
|
||||
LabelButton* m_projectImageLabel;
|
||||
QFrame* m_projectFooter;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -24,8 +24,10 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
public:
|
||||
ProjectInfo() = default;
|
||||
|
||||
ProjectInfo(const QString& path, const QString& projectName, const QString& displayName, const QString& origin,
|
||||
const QString& summary, const QString& imagePath, const QString& backgroundImagePath, bool needsBuild);
|
||||
const QString& summary, const QString& imagePath, const QString& backgroundImagePath, bool needsBuild
|
||||
|
||||
bool operator==(const ProjectInfo& rhs);
|
||||
bool operator!=(const ProjectInfo& rhs);
|
||||
|
||||
@@ -46,9 +48,10 @@ namespace O3DE::ProjectManager
|
||||
QString m_backgroundImagePath;
|
||||
|
||||
// Used in project creation
|
||||
bool m_needsBuild = false; //! Is this a new project or existing
|
||||
|
||||
// Used to flag tags for removal
|
||||
QStringList m_userTagsForRemoval;
|
||||
|
||||
bool m_needsBuild = false; //! Does this project need to be built
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace O3DE::ProjectManager
|
||||
QString m_name;
|
||||
QString m_path;
|
||||
QString m_summary;
|
||||
QStringList m_includedGems;
|
||||
QStringList m_canonicalTags;
|
||||
QStringList m_userTags;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
#include <QFileDialog>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QProgressDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
@@ -192,6 +194,49 @@ namespace O3DE::ProjectManager
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool IsVS2019Installed_internal()
|
||||
{
|
||||
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
|
||||
QString programFilesPath = environment.value("ProgramFiles(x86)");
|
||||
QString vsWherePath = programFilesPath + "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
|
||||
|
||||
QFileInfo vsWhereFile(vsWherePath);
|
||||
if (vsWhereFile.exists() && vsWhereFile.isFile())
|
||||
{
|
||||
QProcess vsWhereProcess;
|
||||
vsWhereProcess.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
vsWhereProcess.start(
|
||||
vsWherePath,
|
||||
QStringList{ "-version", "16.0", "-latest", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"-property", "isComplete" });
|
||||
|
||||
if (!vsWhereProcess.waitForStarted())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (vsWhereProcess.waitForReadyRead())
|
||||
{
|
||||
}
|
||||
|
||||
QString vsWhereOutput(vsWhereProcess.readAllStandardOutput());
|
||||
if (vsWhereOutput.startsWith("1"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsVS2019Installed()
|
||||
{
|
||||
static bool vs2019Installed = IsVS2019Installed_internal();
|
||||
|
||||
return vs2019Installed;
|
||||
}
|
||||
|
||||
ProjectManagerScreen GetProjectManagerScreen(const QString& screen)
|
||||
{
|
||||
auto iter = s_ProjectManagerStringNames.find(screen);
|
||||
@@ -202,6 +247,5 @@ namespace O3DE::ProjectManager
|
||||
|
||||
return ProjectManagerScreen::Invalid;
|
||||
}
|
||||
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -25,6 +25,9 @@ namespace O3DE::ProjectManager
|
||||
bool CopyProject(const QString& origPath, const QString& newPath);
|
||||
bool DeleteProjectFiles(const QString& path, bool force = false);
|
||||
bool MoveProject(const QString& origPath, const QString& newPath, QWidget* parent = nullptr);
|
||||
|
||||
bool IsVS2019Installed();
|
||||
|
||||
ProjectManagerScreen GetProjectManagerScreen(const QString& screen);
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <ProjectButtonWidget.h>
|
||||
#include <PythonBindingsInterface.h>
|
||||
#include <ProjectUtils.h>
|
||||
#include <ProjectBuilder.h>
|
||||
#include <ScreensCtrl.h>
|
||||
|
||||
#include <AzQtComponents/Components/FlowLayout.h>
|
||||
#include <AzCore/Platform.h>
|
||||
@@ -42,6 +44,8 @@
|
||||
#include <QSettings>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include <QQueue>
|
||||
#include <QDir>
|
||||
|
||||
//#define DISPLAY_PROJECT_DEV_DATA true
|
||||
|
||||
@@ -66,6 +70,14 @@ namespace O3DE::ProjectManager
|
||||
m_stack->addWidget(m_projectsContent);
|
||||
|
||||
vLayout->addWidget(m_stack);
|
||||
|
||||
connect(reinterpret_cast<ScreensCtrl*>(parent), &ScreensCtrl::NotifyBuildProject, this, &ProjectsScreen::SuggestBuildProject);
|
||||
}
|
||||
|
||||
ProjectsScreen::~ProjectsScreen()
|
||||
|
||||
{
|
||||
delete m_currentBuilder;
|
||||
}
|
||||
|
||||
QFrame* ProjectsScreen::CreateFirstTimeContent()
|
||||
@@ -110,7 +122,7 @@ namespace O3DE::ProjectManager
|
||||
return frame;
|
||||
}
|
||||
|
||||
QFrame* ProjectsScreen::CreateProjectsContent()
|
||||
QFrame* ProjectsScreen::CreateProjectsContent(QString buildProjectPath, ProjectButton** projectButton)
|
||||
{
|
||||
QFrame* frame = new QFrame(this);
|
||||
frame->setObjectName("projectsContent");
|
||||
@@ -158,30 +170,43 @@ namespace O3DE::ProjectManager
|
||||
projectsScrollArea->setWidgetResizable(true);
|
||||
|
||||
#ifndef DISPLAY_PROJECT_DEV_DATA
|
||||
// Iterate once to insert building project first
|
||||
if (!buildProjectPath.isEmpty())
|
||||
{
|
||||
buildProjectPath = QDir::fromNativeSeparators(buildProjectPath);
|
||||
for (auto project : projectsResult.GetValue())
|
||||
{
|
||||
if (QDir::fromNativeSeparators(project.m_path) == buildProjectPath)
|
||||
{
|
||||
ProjectButton* buildingProjectButton = CreateProjectButton(project, flowLayout, true);
|
||||
|
||||
if (projectButton)
|
||||
{
|
||||
*projectButton = buildingProjectButton;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto project : projectsResult.GetValue())
|
||||
#else
|
||||
ProjectInfo project = projectsResult.GetValue().at(0);
|
||||
for (int i = 0; i < 15; i++)
|
||||
#endif
|
||||
{
|
||||
ProjectButton* projectButton;
|
||||
|
||||
QString projectPreviewPath = project.m_path + m_projectPreviewImagePath;
|
||||
QFileInfo doesPreviewExist(projectPreviewPath);
|
||||
if (doesPreviewExist.exists() && doesPreviewExist.isFile())
|
||||
// Add all other projects skipping building project
|
||||
// Safe if no building project because it is just an empty string
|
||||
if (project.m_path != buildProjectPath)
|
||||
{
|
||||
project.m_imagePath = projectPreviewPath;
|
||||
ProjectButton* projectButtonWidget = CreateProjectButton(project, flowLayout);
|
||||
|
||||
if (RequiresBuildProjectIterator(project.m_path) != m_requiresBuild.end())
|
||||
{
|
||||
projectButtonWidget->ShowBuildButton(true);
|
||||
}
|
||||
}
|
||||
|
||||
projectButton = new ProjectButton(project, this);
|
||||
|
||||
flowLayout->addWidget(projectButton);
|
||||
|
||||
connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject);
|
||||
connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject);
|
||||
connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject);
|
||||
connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject);
|
||||
connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject);
|
||||
}
|
||||
|
||||
layout->addWidget(projectsScrollArea);
|
||||
@@ -191,6 +216,60 @@ namespace O3DE::ProjectManager
|
||||
return frame;
|
||||
}
|
||||
|
||||
ProjectButton* ProjectsScreen::CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing)
|
||||
{
|
||||
ProjectButton* projectButton;
|
||||
|
||||
QString projectPreviewPath = project.m_path + m_projectPreviewImagePath;
|
||||
QFileInfo doesPreviewExist(projectPreviewPath);
|
||||
if (doesPreviewExist.exists() && doesPreviewExist.isFile())
|
||||
{
|
||||
project.m_imagePath = projectPreviewPath;
|
||||
}
|
||||
|
||||
projectButton = new ProjectButton(project, this, processing);
|
||||
|
||||
flowLayout->addWidget(projectButton);
|
||||
|
||||
if (!processing)
|
||||
{
|
||||
connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject);
|
||||
connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject);
|
||||
connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject);
|
||||
connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject);
|
||||
connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject);
|
||||
}
|
||||
connect(projectButton, &ProjectButton::BuildProject, this, &ProjectsScreen::QueueBuildProject);
|
||||
|
||||
return projectButton;
|
||||
}
|
||||
|
||||
void ProjectsScreen::ResetProjectsContent()
|
||||
{
|
||||
// refresh the projects content by re-creating it for now
|
||||
if (m_projectsContent)
|
||||
{
|
||||
m_stack->removeWidget(m_projectsContent);
|
||||
m_projectsContent->deleteLater();
|
||||
}
|
||||
|
||||
// Make sure to update builder with latest Project Button
|
||||
if (m_currentBuilder)
|
||||
{
|
||||
ProjectButton* projectButtonPtr;
|
||||
|
||||
m_projectsContent = CreateProjectsContent(m_currentBuilder->GetProjectPath(), &projectButtonPtr);
|
||||
m_currentBuilder->SetProjectButton(projectButtonPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_projectsContent = CreateProjectsContent();
|
||||
}
|
||||
|
||||
m_stack->addWidget(m_projectsContent);
|
||||
m_stack->setCurrentWidget(m_projectsContent);
|
||||
}
|
||||
|
||||
ProjectManagerScreen ProjectsScreen::GetScreenEnum()
|
||||
{
|
||||
return ProjectManagerScreen::Projects;
|
||||
@@ -237,7 +316,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (ProjectUtils::AddProjectDialog(this))
|
||||
{
|
||||
emit ResetScreenRequest(ProjectManagerScreen::Projects);
|
||||
ResetProjectsContent();
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
}
|
||||
}
|
||||
@@ -245,38 +324,47 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (!projectPath.isEmpty())
|
||||
{
|
||||
AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory();
|
||||
AZStd::string executableFilename = "Editor";
|
||||
AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION);
|
||||
auto cmdPath = AZ::IO::FixedMaxPathString::format("%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(), projectPath.toStdString().c_str());
|
||||
if (!WarnIfInBuildQueue(projectPath))
|
||||
{
|
||||
AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory();
|
||||
AZStd::string executableFilename = "Editor";
|
||||
AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION);
|
||||
auto cmdPath = AZ::IO::FixedMaxPathString::format(
|
||||
"%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(),
|
||||
projectPath.toStdString().c_str());
|
||||
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
processLaunchInfo.m_commandlineParameters = cmdPath;
|
||||
bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
|
||||
if (!launchSucceeded)
|
||||
{
|
||||
AZ_Error("ProjectManager", false, "Failed to launch editor");
|
||||
QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid."));
|
||||
}
|
||||
else
|
||||
{
|
||||
// prevent the user from accidentally pressing the button while the editor is launching
|
||||
// and let them know what's happening
|
||||
ProjectButton* button = qobject_cast<ProjectButton*>(sender());
|
||||
if (button)
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
processLaunchInfo.m_commandlineParameters = cmdPath;
|
||||
bool launchSucceeded = AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
|
||||
if (!launchSucceeded)
|
||||
{
|
||||
button->SetButtonEnabled(false);
|
||||
button->SetButtonOverlayText(tr("Opening Editor..."));
|
||||
AZ_Error("ProjectManager", false, "Failed to launch editor");
|
||||
QMessageBox::critical(
|
||||
this, tr("Error"), tr("Failed to launch the Editor, please verify the project settings are valid."));
|
||||
}
|
||||
else
|
||||
{
|
||||
// prevent the user from accidentally pressing the button while the editor is launching
|
||||
// and let them know what's happening
|
||||
ProjectButton* button = qobject_cast<ProjectButton*>(sender());
|
||||
if (button)
|
||||
{
|
||||
button->SetLaunchButtonEnabled(false);
|
||||
button->SetButtonOverlayText(tr("Opening Editor..."));
|
||||
}
|
||||
|
||||
// enable the button after 3 seconds
|
||||
constexpr int waitTimeInMs = 3000;
|
||||
QTimer::singleShot(waitTimeInMs, this, [this, button] {
|
||||
if (button)
|
||||
// enable the button after 3 seconds
|
||||
constexpr int waitTimeInMs = 3000;
|
||||
QTimer::singleShot(
|
||||
waitTimeInMs, this,
|
||||
[this, button]
|
||||
{
|
||||
button->SetButtonEnabled(true);
|
||||
}
|
||||
});
|
||||
if (button)
|
||||
{
|
||||
button->SetLaunchButtonEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -288,38 +376,90 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
void ProjectsScreen::HandleEditProject(const QString& projectPath)
|
||||
{
|
||||
emit NotifyCurrentProject(projectPath);
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject);
|
||||
if (!WarnIfInBuildQueue(projectPath))
|
||||
{
|
||||
emit NotifyCurrentProject(projectPath);
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject);
|
||||
}
|
||||
}
|
||||
void ProjectsScreen::HandleCopyProject(const QString& projectPath)
|
||||
{
|
||||
// Open file dialog and choose location for copied project then register copy with O3DE
|
||||
if (ProjectUtils::CopyProjectDialog(projectPath, this))
|
||||
if (!WarnIfInBuildQueue(projectPath))
|
||||
{
|
||||
emit ResetScreenRequest(ProjectManagerScreen::Projects);
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
// Open file dialog and choose location for copied project then register copy with O3DE
|
||||
if (ProjectUtils::CopyProjectDialog(projectPath, this))
|
||||
{
|
||||
ResetProjectsContent();
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
}
|
||||
}
|
||||
}
|
||||
void ProjectsScreen::HandleRemoveProject(const QString& projectPath)
|
||||
{
|
||||
// Unregister Project from O3DE and reload projects
|
||||
if (ProjectUtils::UnregisterProject(projectPath))
|
||||
if (!WarnIfInBuildQueue(projectPath))
|
||||
{
|
||||
emit ResetScreenRequest(ProjectManagerScreen::Projects);
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
// Unregister Project from O3DE and reload projects
|
||||
if (ProjectUtils::UnregisterProject(projectPath))
|
||||
{
|
||||
ResetProjectsContent();
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
}
|
||||
}
|
||||
}
|
||||
void ProjectsScreen::HandleDeleteProject(const QString& projectPath)
|
||||
{
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(
|
||||
this, tr("Delete Project"), tr("Are you sure?\nProject will be removed from O3DE and directory will be deleted!"),
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (warningResult == QMessageBox::Yes)
|
||||
if (!WarnIfInBuildQueue(projectPath))
|
||||
{
|
||||
// Remove project from O3DE and delete from disk
|
||||
HandleRemoveProject(projectPath);
|
||||
ProjectUtils::DeleteProjectFiles(projectPath);
|
||||
QMessageBox::StandardButton warningResult = QMessageBox::warning(this,
|
||||
tr("Delete Project"),
|
||||
tr("Are you sure?\nProject will be unregistered from O3DE and project directory will be deleted from your disk."),
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (warningResult == QMessageBox::Yes)
|
||||
{
|
||||
// Remove project from O3DE and delete from disk
|
||||
HandleRemoveProject(projectPath);
|
||||
ProjectUtils::DeleteProjectFiles(projectPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectsScreen::SuggestBuildProject(const ProjectInfo& projectInfo)
|
||||
{
|
||||
if (projectInfo.m_needsBuild)
|
||||
{
|
||||
if (RequiresBuildProjectIterator(projectInfo.m_path) == m_requiresBuild.end())
|
||||
{
|
||||
m_requiresBuild.append(projectInfo);
|
||||
}
|
||||
ResetProjectsContent();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::information(this,
|
||||
tr("Project Should be rebuilt."),
|
||||
projectInfo.m_projectName + tr(" project likely needs to be rebuilt."));
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectsScreen::QueueBuildProject(const ProjectInfo& projectInfo)
|
||||
{
|
||||
auto requiredIter = RequiresBuildProjectIterator(projectInfo.m_path);
|
||||
if (requiredIter != m_requiresBuild.end())
|
||||
{
|
||||
m_requiresBuild.erase(requiredIter);
|
||||
}
|
||||
|
||||
if (!BuildQueueContainsProject(projectInfo.m_path))
|
||||
{
|
||||
if (m_buildQueue.empty() && !m_currentBuilder)
|
||||
{
|
||||
StartProjectBuild(projectInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buildQueue.append(projectInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,17 +471,7 @@ namespace O3DE::ProjectManager
|
||||
}
|
||||
else
|
||||
{
|
||||
// refresh the projects content by re-creating it for now
|
||||
if (m_projectsContent)
|
||||
{
|
||||
m_stack->removeWidget(m_projectsContent);
|
||||
m_projectsContent->deleteLater();
|
||||
}
|
||||
|
||||
m_projectsContent = CreateProjectsContent();
|
||||
|
||||
m_stack->addWidget(m_projectsContent);
|
||||
m_stack->setCurrentWidget(m_projectsContent);
|
||||
ResetProjectsContent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,4 +493,89 @@ namespace O3DE::ProjectManager
|
||||
return displayFirstTimeContent;
|
||||
}
|
||||
|
||||
void ProjectsScreen::StartProjectBuild(const ProjectInfo& projectInfo)
|
||||
{
|
||||
if (ProjectUtils::IsVS2019Installed())
|
||||
{
|
||||
QMessageBox::StandardButton buildProject = QMessageBox::information(
|
||||
this,
|
||||
tr("Building \"%1\"").arg(projectInfo.m_projectName),
|
||||
tr("Ready to build \"%1\"?").arg(projectInfo.m_projectName),
|
||||
QMessageBox::No | QMessageBox::Yes);
|
||||
|
||||
if (buildProject == QMessageBox::Yes)
|
||||
{
|
||||
m_currentBuilder = new ProjectBuilderController(projectInfo, nullptr, this);
|
||||
ResetProjectsContent();
|
||||
connect(m_currentBuilder, &ProjectBuilderController::Done, this, &ProjectsScreen::ProjectBuildDone);
|
||||
|
||||
m_currentBuilder->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
ProjectBuildDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectsScreen::ProjectBuildDone()
|
||||
{
|
||||
delete m_currentBuilder;
|
||||
m_currentBuilder = nullptr;
|
||||
|
||||
if (!m_buildQueue.empty())
|
||||
{
|
||||
StartProjectBuild(m_buildQueue.front());
|
||||
m_buildQueue.pop_front();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetProjectsContent();
|
||||
}
|
||||
}
|
||||
|
||||
QList<ProjectInfo>::iterator ProjectsScreen::RequiresBuildProjectIterator(const QString& projectPath)
|
||||
{
|
||||
QString nativeProjPath(QDir::toNativeSeparators(projectPath));
|
||||
auto projectIter = m_requiresBuild.begin();
|
||||
for (; projectIter != m_requiresBuild.end(); ++projectIter)
|
||||
{
|
||||
if (QDir::toNativeSeparators(projectIter->m_path) == nativeProjPath)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return projectIter;
|
||||
}
|
||||
|
||||
bool ProjectsScreen::BuildQueueContainsProject(const QString& projectPath)
|
||||
{
|
||||
QString nativeProjPath(QDir::toNativeSeparators(projectPath));
|
||||
for (const ProjectInfo& project : m_buildQueue)
|
||||
{
|
||||
if (QDir::toNativeSeparators(project.m_path) == nativeProjPath)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProjectsScreen::WarnIfInBuildQueue(const QString& projectPath)
|
||||
{
|
||||
if (BuildQueueContainsProject(projectPath))
|
||||
{
|
||||
QMessageBox::warning(
|
||||
this,
|
||||
tr("Action Temporarily Disabled!"),
|
||||
tr("Action not allowed on projects in build queue."));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -13,21 +13,28 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ScreenWidget.h>
|
||||
#include <ProjectInfo.h>
|
||||
|
||||
#include <QQueue>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QPaintEvent)
|
||||
QT_FORWARD_DECLARE_CLASS(QFrame)
|
||||
QT_FORWARD_DECLARE_CLASS(QStackedWidget)
|
||||
QT_FORWARD_DECLARE_CLASS(QLayout)
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
QT_FORWARD_DECLARE_CLASS(ProjectBuilderController);
|
||||
QT_FORWARD_DECLARE_CLASS(ProjectButton);
|
||||
|
||||
class ProjectsScreen
|
||||
: public ScreenWidget
|
||||
{
|
||||
|
||||
public:
|
||||
explicit ProjectsScreen(QWidget* parent = nullptr);
|
||||
~ProjectsScreen() = default;
|
||||
~ProjectsScreen();
|
||||
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
QString GetTabText() override;
|
||||
@@ -35,6 +42,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
protected:
|
||||
void NotifyCurrentScreen() override;
|
||||
void ProjectBuildDone();
|
||||
|
||||
protected slots:
|
||||
void HandleNewProjectButton();
|
||||
@@ -45,19 +53,32 @@ namespace O3DE::ProjectManager
|
||||
void HandleRemoveProject(const QString& projectPath);
|
||||
void HandleDeleteProject(const QString& projectPath);
|
||||
|
||||
void SuggestBuildProject(const ProjectInfo& projectInfo);
|
||||
void QueueBuildProject(const ProjectInfo& projectInfo);
|
||||
|
||||
void paintEvent(QPaintEvent* event) override;
|
||||
|
||||
private:
|
||||
QFrame* CreateFirstTimeContent();
|
||||
QFrame* CreateProjectsContent();
|
||||
QFrame* CreateProjectsContent(QString buildProjectPath = "", ProjectButton** projectButton = nullptr);
|
||||
ProjectButton* CreateProjectButton(ProjectInfo& project, QLayout* flowLayout, bool processing = false);
|
||||
void ResetProjectsContent();
|
||||
bool ShouldDisplayFirstTimeContent();
|
||||
|
||||
QAction* m_createNewProjectAction;
|
||||
QAction* m_addExistingProjectAction;
|
||||
void StartProjectBuild(const ProjectInfo& projectInfo);
|
||||
QList<ProjectInfo>::iterator RequiresBuildProjectIterator(const QString& projectPath);
|
||||
bool BuildQueueContainsProject(const QString& projectPath);
|
||||
bool WarnIfInBuildQueue(const QString& projectPath);
|
||||
|
||||
QAction* m_createNewProjectAction = nullptr;
|
||||
QAction* m_addExistingProjectAction = nullptr;
|
||||
QPixmap m_background;
|
||||
QFrame* m_firstTimeContent;
|
||||
QFrame* m_projectsContent;
|
||||
QStackedWidget* m_stack;
|
||||
QFrame* m_firstTimeContent = nullptr;
|
||||
QFrame* m_projectsContent = nullptr;
|
||||
QStackedWidget* m_stack = nullptr;
|
||||
QList<ProjectInfo> m_requiresBuild;
|
||||
QQueue<ProjectInfo> m_buildQueue;
|
||||
ProjectBuilderController* m_currentBuilder = nullptr;
|
||||
|
||||
const QString m_projectPreviewImagePath = "/preview.png";
|
||||
|
||||
|
||||
@@ -669,7 +669,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
ProjectInfo projectInfo;
|
||||
projectInfo.m_path = Py_To_String(path);
|
||||
projectInfo.m_isNew = false;
|
||||
projectInfo.m_needsBuild = false;
|
||||
|
||||
auto projectData = m_manifest.attr("get_project_json_data")(pybind11::none(), path);
|
||||
if (pybind11::isinstance<pybind11::dict>(projectData))
|
||||
@@ -779,7 +779,7 @@ namespace O3DE::ProjectManager
|
||||
ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path)
|
||||
{
|
||||
ProjectTemplateInfo templateInfo;
|
||||
templateInfo.m_path = Py_To_String(path);
|
||||
templateInfo.m_path = Py_To_String(pybind11::str(path));
|
||||
|
||||
auto data = m_manifest.attr("get_template_json_data")(pybind11::none(), path);
|
||||
if (pybind11::isinstance<pybind11::dict>(data))
|
||||
@@ -806,6 +806,13 @@ namespace O3DE::ProjectManager
|
||||
templateInfo.m_canonicalTags.push_back(Py_To_String(tag));
|
||||
}
|
||||
}
|
||||
if (data.contains("included_gems"))
|
||||
{
|
||||
for (auto gem : data["included_gems"])
|
||||
{
|
||||
templateInfo.m_includedGems.push_back(Py_To_String(gem));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ([[maybe_unused]] const std::exception& e)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ScreenDefs.h>
|
||||
#include <ProjectInfo.h>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QStyleOption>
|
||||
@@ -61,6 +62,7 @@ namespace O3DE::ProjectManager
|
||||
void GotoPreviousScreenRequest();
|
||||
void ResetScreenRequest(ProjectManagerScreen screen);
|
||||
void NotifyCurrentProject(const QString& projectPath);
|
||||
void NotifyBuildProject(const ProjectInfo& projectInfo);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@ namespace O3DE::ProjectManager
|
||||
connect(newScreen, &ScreenWidget::GotoPreviousScreenRequest, this, &ScreensCtrl::GotoPreviousScreen);
|
||||
connect(newScreen, &ScreenWidget::ResetScreenRequest, this, &ScreensCtrl::ResetScreen);
|
||||
connect(newScreen, &ScreenWidget::NotifyCurrentProject, this, &ScreensCtrl::NotifyCurrentProject);
|
||||
connect(newScreen, &ScreenWidget::NotifyBuildProject, this, &ScreensCtrl::NotifyBuildProject);
|
||||
}
|
||||
|
||||
void ScreensCtrl::ResetAllScreens()
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <ScreenDefs.h>
|
||||
#include <ProjectInfo.h>
|
||||
|
||||
#include <QStackedWidget>
|
||||
#include <QStack>
|
||||
@@ -39,6 +40,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
signals:
|
||||
void NotifyCurrentProject(const QString& projectPath);
|
||||
void NotifyBuildProject(const ProjectInfo& projectInfo);
|
||||
|
||||
public slots:
|
||||
bool ChangeToScreen(ProjectManagerScreen screen);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <TemplateButtonWidget.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
#include <QAbstractButton>
|
||||
#include <QStyle>
|
||||
#include <QVariant>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
|
||||
TemplateButton::TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent)
|
||||
: QPushButton(parent)
|
||||
{
|
||||
setAutoExclusive(true);
|
||||
|
||||
setObjectName("templateButton");
|
||||
|
||||
QVBoxLayout* vLayout = new QVBoxLayout();
|
||||
vLayout->setSpacing(0);
|
||||
vLayout->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(vLayout);
|
||||
|
||||
QLabel* image = new QLabel(this);
|
||||
image->setObjectName("templateImage");
|
||||
image->setPixmap(
|
||||
QPixmap(imagePath).scaled(QSize(s_templateImageWidth,s_templateImageHeight) , Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
vLayout->addWidget(image);
|
||||
|
||||
QLabel* label = new QLabel(labelText, this);
|
||||
label->setObjectName("templateLabel");
|
||||
vLayout->addWidget(label);
|
||||
|
||||
connect(this, &QAbstractButton::toggled, this, &TemplateButton::onToggled);
|
||||
}
|
||||
|
||||
void TemplateButton::onToggled()
|
||||
{
|
||||
setProperty("Checked", isChecked());
|
||||
|
||||
// we must unpolish/polish every child after changing a property
|
||||
// or else they won't use the correct stylesheet selector
|
||||
for (auto child : findChildren<QWidget*>())
|
||||
{
|
||||
child->style()->unpolish(child);
|
||||
child->style()->polish(child);
|
||||
}
|
||||
|
||||
style()->unpolish(this);
|
||||
style()->polish(this);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QPushButton>
|
||||
#endif
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
class TemplateButton
|
||||
: public QPushButton
|
||||
{
|
||||
Q_OBJECT // AUTOMOC
|
||||
|
||||
public:
|
||||
explicit TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent = nullptr);
|
||||
~TemplateButton() = default;
|
||||
|
||||
protected slots:
|
||||
void onToggled();
|
||||
|
||||
private:
|
||||
inline constexpr static int s_templateImageWidth = 92;
|
||||
inline constexpr static int s_templateImageHeight = 122;
|
||||
};
|
||||
} // namespace O3DE::ProjectManager
|
||||
@@ -119,7 +119,9 @@ namespace O3DE::ProjectManager
|
||||
|
||||
void UpdateProjectCtrl::HandleNextButton()
|
||||
{
|
||||
if (m_stack->currentIndex() == ScreenOrder::Settings)
|
||||
bool shouldRebuild = false;
|
||||
|
||||
if (m_stack->currentIndex() == ScreenOrder::Settings && m_updateSettingsScreen)
|
||||
{
|
||||
if (m_updateSettingsScreen)
|
||||
{
|
||||
@@ -155,11 +157,17 @@ namespace O3DE::ProjectManager
|
||||
m_projectInfo = newProjectSettings;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen)
|
||||
else if (m_stack->currentIndex() == ScreenOrder::Gems && m_gemCatalogScreen)
|
||||
{
|
||||
// Enable or disable the gems that got adjusted in the gem catalog and apply them to the given project.
|
||||
m_gemCatalogScreen->EnableDisableGemsForProject(m_projectInfo.m_path);
|
||||
|
||||
shouldRebuild = true;
|
||||
}
|
||||
|
||||
if (shouldRebuild)
|
||||
{
|
||||
emit NotifyBuildProject(m_projectInfo);
|
||||
}
|
||||
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
|
||||
@@ -38,6 +38,8 @@ set(FILES
|
||||
Source/ProjectInfo.cpp
|
||||
Source/ProjectUtils.h
|
||||
Source/ProjectUtils.cpp
|
||||
Source/ProjectBuilder.h
|
||||
Source/ProjectBuilder.cpp
|
||||
Source/UpdateProjectSettingsScreen.h
|
||||
Source/UpdateProjectSettingsScreen.cpp
|
||||
Source/NewProjectSettingsScreen.h
|
||||
@@ -60,6 +62,8 @@ set(FILES
|
||||
Source/LinkWidget.cpp
|
||||
Source/TagWidget.h
|
||||
Source/TagWidget.cpp
|
||||
Source/TemplateButtonWidget.h
|
||||
Source/TemplateButtonWidget.cpp
|
||||
Source/GemCatalog/GemCatalogHeaderWidget.h
|
||||
Source/GemCatalog/GemCatalogHeaderWidget.cpp
|
||||
Source/GemCatalog/GemCatalogScreen.h
|
||||
|
||||
Reference in New Issue
Block a user