Merge branch 'main' into ly-as-sdk/LYN-2948

# Conflicts:
#	Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/gem.json
This commit is contained in:
pappeste
2021-05-27 10:39:37 -07:00
290 changed files with 4267 additions and 4915 deletions
@@ -14,11 +14,17 @@
#include <ScreensCtrl.h>
#include <PythonBindingsInterface.h>
#include <NewProjectSettingsScreen.h>
#include <ScreenHeaderWidget.h>
#include <GemCatalog/GemCatalogScreen.h>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QMessageBox>
#include <QStackedWidget>
#include <QLabel>
#include <QSizePolicy>
namespace O3DE::ProjectManager
{
@@ -26,29 +32,34 @@ namespace O3DE::ProjectManager
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
setLayout(vLayout);
vLayout->setContentsMargins(0,0,0,0);
m_screensCtrl = new ScreensCtrl();
vLayout->addWidget(m_screensCtrl);
m_header = new ScreenHeader(this);
m_header->setTitle(tr("Create a New Project"));
m_header->setSubTitle(tr("Enter Project Details"));
connect(m_header->backButton(), &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton);
vLayout->addWidget(m_header);
m_stack = new QStackedWidget(this);
m_stack->setObjectName("body");
m_stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred,QSizePolicy::Expanding));
m_stack->addWidget(new NewProjectSettingsScreen());
m_stack->addWidget(new GemCatalogScreen());
vLayout->addWidget(m_stack);
QDialogButtonBox* backNextButtons = new QDialogButtonBox();
backNextButtons->setObjectName("footer");
vLayout->addWidget(backNextButtons);
m_backButton = backNextButtons->addButton(tr("Back"), QDialogButtonBox::RejectRole);
m_backButton->setProperty("secondary", true);
m_nextButton = backNextButtons->addButton(tr("Next"), QDialogButtonBox::ApplyRole);
connect(m_backButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleBackButton);
connect(m_nextButton, &QPushButton::pressed, this, &CreateProjectCtrl::HandleNextButton);
connect(m_backButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleBackButton);
connect(m_nextButton, &QPushButton::clicked, this, &CreateProjectCtrl::HandleNextButton);
m_screensOrder =
{
ProjectManagerScreen::NewProjectSettings,
ProjectManagerScreen::GemCatalog
};
m_screensCtrl->BuildScreens(m_screensOrder);
m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::NewProjectSettings, false);
UpdateNextButtonText();
Update();
setLayout(vLayout);
}
ProjectManagerScreen CreateProjectCtrl::GetScreenEnum()
@@ -58,28 +69,20 @@ namespace O3DE::ProjectManager
void CreateProjectCtrl::HandleBackButton()
{
if (!m_screensCtrl->GotoPreviousScreen())
if (m_stack->currentIndex() > 0)
{
emit GotoPreviousScreenRequest();
m_stack->setCurrentIndex(m_stack->currentIndex() - 1);
Update();
}
else
{
UpdateNextButtonText();
emit GotoPreviousScreenRequest();
}
}
void CreateProjectCtrl::HandleNextButton()
{
ScreenWidget* currentScreen = m_screensCtrl->GetCurrentScreen();
ScreenWidget* currentScreen = reinterpret_cast<ScreenWidget*>(m_stack->currentWidget());
ProjectManagerScreen screenEnum = currentScreen->GetScreenEnum();
auto screenOrderIter = m_screensOrder.begin();
for (; screenOrderIter != m_screensOrder.end(); ++screenOrderIter)
{
if (*screenOrderIter == screenEnum)
{
++screenOrderIter;
break;
}
}
if (screenEnum == ProjectManagerScreen::NewProjectSettings)
{
@@ -97,10 +100,10 @@ namespace O3DE::ProjectManager
}
}
if (screenOrderIter != m_screensOrder.end())
if (m_stack->currentIndex() != m_stack->count() - 1)
{
m_screensCtrl->ChangeToScreen(*screenOrderIter);
UpdateNextButtonText();
m_stack->setCurrentIndex(m_stack->currentIndex() + 1);
Update();
}
else
{
@@ -108,7 +111,7 @@ namespace O3DE::ProjectManager
if (result.IsSuccess())
{
// adding gems is not implemented yet because we don't know what targets to add or how to add them
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
}
else
{
@@ -117,14 +120,21 @@ namespace O3DE::ProjectManager
}
}
void CreateProjectCtrl::UpdateNextButtonText()
void CreateProjectCtrl::Update()
{
QString nextButtonText = tr("Next");
if (m_screensCtrl->GetCurrentScreen()->GetScreenEnum() == ProjectManagerScreen::GemCatalog)
ScreenWidget* currentScreen = reinterpret_cast<ScreenWidget*>(m_stack->currentWidget());
if (currentScreen && currentScreen->GetScreenEnum() == ProjectManagerScreen::GemCatalog)
{
nextButtonText = tr("Create Project");
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"));
}
m_nextButton->setText(nextButtonText);
}
} // namespace O3DE::ProjectManager
@@ -12,15 +12,18 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include "ProjectInfo.h"
#include <ScreenWidget.h>
#include <ScreensCtrl.h>
#include <QPushButton>
#include <ProjectInfo.h>
#endif
QT_FORWARD_DECLARE_CLASS(QStackedWidget)
QT_FORWARD_DECLARE_CLASS(QPushButton)
QT_FORWARD_DECLARE_CLASS(QLabel)
namespace O3DE::ProjectManager
{
QT_FORWARD_DECLARE_CLASS(ScreenHeader)
class CreateProjectCtrl
: public ScreenWidget
{
@@ -34,12 +37,13 @@ namespace O3DE::ProjectManager
void HandleNextButton();
private:
void UpdateNextButtonText();
void Update();
QStackedWidget* m_stack;
ScreenHeader* m_header;
ScreensCtrl* m_screensCtrl;
QPushButton* m_backButton;
QPushButton* m_nextButton;
QVector<ProjectManagerScreen> m_screensOrder;
QString m_projectTemplatePath;
ProjectInfo m_projectInfo;
@@ -82,6 +82,16 @@ namespace O3DE::ProjectManager
return ProjectManagerScreen::EngineSettings;
}
QString EngineSettingsScreen::GetTabText()
{
return tr("Engine");
}
bool EngineSettingsScreen::IsTab()
{
return true;
}
void EngineSettingsScreen::OnTextChanged()
{
// save engine settings
@@ -26,7 +26,10 @@ namespace O3DE::ProjectManager
public:
explicit EngineSettingsScreen(QWidget* parent = nullptr);
~EngineSettingsScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
QString GetTabText() override;
bool IsTab() override;
protected slots:
void OnTextChanged();
@@ -1,95 +0,0 @@
/*
* 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 <FirstTimeUseScreen.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QIcon>
#include <QSpacerItem>
namespace O3DE::ProjectManager
{
FirstTimeUseScreen::FirstTimeUseScreen(QWidget* parent)
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
setLayout(vLayout);
vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins);
QLabel* titleLabel = new QLabel(this);
titleLabel->setText(tr("Ready. Set. Create!"));
titleLabel->setStyleSheet("font-size: 60px");
vLayout->addWidget(titleLabel);
QLabel* introLabel = new QLabel(this);
introLabel->setTextFormat(Qt::AutoText);
introLabel->setText(tr("<html><head/><body><p>Welcome to O3DE! Start something new by creating a project. Not sure what to create? </p><p>Explore what\342\200\231s available by downloading our sample project.</p></body></html>"));
introLabel->setStyleSheet("font-size: 14px");
vLayout->addWidget(introLabel);
QHBoxLayout* buttonLayout = new QHBoxLayout();
buttonLayout->setSpacing(s_buttonSpacing);
m_createProjectButton = CreateLargeBoxButton(QIcon(":/Add.svg"), tr("Create Project"), this);
m_createProjectButton->setIconSize(QSize(s_iconSize, s_iconSize));
buttonLayout->addWidget(m_createProjectButton);
m_addProjectButton = CreateLargeBoxButton(QIcon(":/Select_Folder.svg"), tr("Add a Project"), this);
m_addProjectButton->setIconSize(QSize(s_iconSize, s_iconSize));
buttonLayout->addWidget(m_addProjectButton);
QSpacerItem* buttonSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum);
buttonLayout->addItem(buttonSpacer);
vLayout->addItem(buttonLayout);
QSpacerItem* verticalSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Minimum, QSizePolicy::Expanding);
vLayout->addItem(verticalSpacer);
// Using border-image allows for scaling options background-image does not support
setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }");
connect(m_createProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleNewProjectButton);
connect(m_addProjectButton, &QPushButton::pressed, this, &FirstTimeUseScreen::HandleAddProjectButton);
}
ProjectManagerScreen FirstTimeUseScreen::GetScreenEnum()
{
return ProjectManagerScreen::FirstTimeUse;
}
void FirstTimeUseScreen::HandleNewProjectButton()
{
emit ResetScreenRequest(ProjectManagerScreen::CreateProject);
emit ChangeScreenRequest(ProjectManagerScreen::CreateProject);
}
void FirstTimeUseScreen::HandleAddProjectButton()
{
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
}
QPushButton* FirstTimeUseScreen::CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent)
{
QPushButton* largeBoxButton = new QPushButton(icon, text, parent);
largeBoxButton->setFixedSize(s_boxButtonWidth, s_boxButtonHeight);
largeBoxButton->setFlat(true);
largeBoxButton->setFocusPolicy(Qt::FocusPolicy::NoFocus);
largeBoxButton->setStyleSheet("QPushButton { font-size: 14px; background-color: rgba(0, 0, 0, 191); }");
return largeBoxButton;
}
} // namespace O3DE::ProjectManager
@@ -1,49 +0,0 @@
/*
* 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 <ScreenWidget.h>
#endif
QT_FORWARD_DECLARE_CLASS(QIcon)
QT_FORWARD_DECLARE_CLASS(QPushButton)
namespace O3DE::ProjectManager
{
class FirstTimeUseScreen
: public ScreenWidget
{
public:
explicit FirstTimeUseScreen(QWidget* parent = nullptr);
~FirstTimeUseScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
protected slots:
void HandleNewProjectButton();
void HandleAddProjectButton();
private:
QPushButton* CreateLargeBoxButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr);
QPushButton* m_createProjectButton;
QPushButton* m_addProjectButton;
inline constexpr static int s_contentMargins = 80;
inline constexpr static int s_buttonSpacing = 30;
inline constexpr static int s_iconSize = 24;
inline constexpr static int s_spacerSize = 20;
inline constexpr static int s_boxButtonWidth = 210;
inline constexpr static int s_boxButtonHeight = 280;
};
} // namespace O3DE::ProjectManager
@@ -12,6 +12,9 @@
#include <NewProjectSettingsScreen.h>
#include <PythonBindingsInterface.h>
#include <FormLineEditWidget.h>
#include <FormBrowseEditWidget.h>
#include <PathValidator.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
@@ -23,6 +26,7 @@
#include <QPushButton>
#include <QSpacerItem>
#include <QStandardPaths>
#include <QFrame>
namespace O3DE::ProjectManager
{
@@ -31,64 +35,81 @@ namespace O3DE::ProjectManager
NewProjectSettingsScreen::NewProjectSettingsScreen(QWidget* parent)
: ScreenWidget(parent)
{
QHBoxLayout* hLayout = new QHBoxLayout();
this->setLayout(hLayout);
QHBoxLayout* hLayout = new QHBoxLayout(this);
hLayout->setAlignment(Qt::AlignLeft);
hLayout->setContentsMargins(0,0,0,0);
// if we don't provide a parent for this box layout the stylesheet doesn't take
// if we don't set this in a frame (just use a sub-layout) all the content will align incorrectly horizontally
QFrame* projectSettingsFrame = new QFrame(this);
projectSettingsFrame->setObjectName("projectSettings");
QVBoxLayout* vLayout = new QVBoxLayout(this);
QLabel* projectNameLabel = new QLabel(tr("Project Name"), this);
vLayout->addWidget(projectNameLabel);
m_projectNameLineEdit = new QLineEdit(tr("New Project"), this);
vLayout->addWidget(m_projectNameLineEdit);
QLabel* projectPathLabel = new QLabel(tr("Project Location"), this);
vLayout->addWidget(projectPathLabel);
// you cannot remove content margins in qss
vLayout->setContentsMargins(0,0,0,0);
vLayout->setAlignment(Qt::AlignTop);
{
QHBoxLayout* projectPathLayout = new QHBoxLayout(this);
m_projectName = new FormLineEditWidget(tr("Project name"), tr("New Project"), this);
m_projectName->setErrorLabelText(
tr("A project with this name already exists at this location. Please choose a new name or location."));
vLayout->addWidget(m_projectName);
m_projectPathLineEdit = new QLineEdit(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this);
projectPathLayout->addWidget(m_projectPathLineEdit);
m_projectPath =
new FormBrowseEditWidget(tr("Project Location"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this);
m_projectPath->lineEdit()->setReadOnly(true);
m_projectPath->setErrorLabelText(tr("Please provide a valid path to a folder that exists"));
m_projectPath->lineEdit()->setValidator(new PathValidator(PathValidator::PathMode::ExistingFolder, this));
vLayout->addWidget(m_projectPath);
QPushButton* browseButton = new QPushButton(tr("Browse"), this);
connect(browseButton, &QPushButton::pressed, this, &NewProjectSettingsScreen::HandleBrowseButton);
projectPathLayout->addWidget(browseButton);
vLayout->addLayout(projectPathLayout);
}
QLabel* projectTemplateLabel = new QLabel(this);
projectTemplateLabel->setText("Project Template");
vLayout->addWidget(projectTemplateLabel);
QHBoxLayout* templateLayout = new QHBoxLayout(this);
vLayout->addItem(templateLayout);
m_projectTemplateButtonGroup = new QButtonGroup(this);
auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates();
if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty())
{
for (auto projectTemplate : templatesResult.GetValue())
// if we don't use a QFrame we cannot "contain" the widgets inside and move them around
// as a group
QFrame* projectTemplateWidget = new QFrame(this);
projectTemplateWidget->setObjectName("projectTemplate");
QVBoxLayout* containerLayout = new QVBoxLayout();
containerLayout->setAlignment(Qt::AlignTop);
{
QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this);
radioButton->setProperty(k_pathProperty, projectTemplate.m_path);
m_projectTemplateButtonGroup->addButton(radioButton);
QLabel* projectTemplateLabel = new QLabel(tr("Select a Project Template"));
projectTemplateLabel->setObjectName("projectTemplateLabel");
containerLayout->addWidget(projectTemplateLabel);
templateLayout->addWidget(radioButton);
QLabel* projectTemplateDetailsLabel = new QLabel(tr("Project templates are pre-configured with relevant Gems that provide "
"additional functionality and content to the project."));
projectTemplateDetailsLabel->setWordWrap(true);
projectTemplateDetailsLabel->setObjectName("projectTemplateDetailsLabel");
containerLayout->addWidget(projectTemplateDetailsLabel);
QHBoxLayout* templateLayout = new QHBoxLayout(this);
containerLayout->addItem(templateLayout);
m_projectTemplateButtonGroup = new QButtonGroup(this);
m_projectTemplateButtonGroup->setObjectName("templateButtonGroup");
auto templatesResult = PythonBindingsInterface::Get()->GetProjectTemplates();
if (templatesResult.IsSuccess() && !templatesResult.GetValue().isEmpty())
{
for (auto projectTemplate : templatesResult.GetValue())
{
QRadioButton* radioButton = new QRadioButton(projectTemplate.m_name, this);
radioButton->setProperty(k_pathProperty, projectTemplate.m_path);
m_projectTemplateButtonGroup->addButton(radioButton);
containerLayout->addWidget(radioButton);
}
m_projectTemplateButtonGroup->buttons().first()->setChecked(true);
}
}
m_projectTemplateButtonGroup->buttons().first()->setChecked(true);
projectTemplateWidget->setLayout(containerLayout);
vLayout->addWidget(projectTemplateWidget);
}
projectSettingsFrame->setLayout(vLayout);
QSpacerItem* verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
vLayout->addItem(verticalSpacer);
hLayout->addWidget(projectSettingsFrame);
hLayout->addItem(vLayout);
QWidget* projectTemplateDetails = new QWidget(this);
projectTemplateDetails->setObjectName("projectTemplateDetails");
hLayout->addWidget(projectTemplateDetails);
QWidget* gemsListPlaceholder = new QWidget(this);
gemsListPlaceholder->setFixedWidth(250);
hLayout->addWidget(gemsListPlaceholder);
this->setLayout(hLayout);
}
ProjectManagerScreen NewProjectSettingsScreen::GetScreenEnum()
@@ -96,26 +117,12 @@ namespace O3DE::ProjectManager
return ProjectManagerScreen::NewProjectSettings;
}
void NewProjectSettingsScreen::HandleBrowseButton()
{
QString defaultPath = m_projectPathLineEdit->text();
if (defaultPath.isEmpty())
{
defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
QString directory = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("New project path"), defaultPath));
if (!directory.isEmpty())
{
m_projectPathLineEdit->setText(directory);
}
}
ProjectInfo NewProjectSettingsScreen::GetProjectInfo()
{
ProjectInfo projectInfo;
projectInfo.m_projectName = m_projectNameLineEdit->text();
projectInfo.m_path = QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + projectInfo.m_projectName);
projectInfo.m_projectName = m_projectName->lineEdit()->text();
projectInfo.m_path = QDir::toNativeSeparators(m_projectPath->lineEdit()->text() + "/" + projectInfo.m_projectName);
return projectInfo;
}
@@ -127,18 +134,18 @@ namespace O3DE::ProjectManager
bool NewProjectSettingsScreen::Validate()
{
bool projectNameIsValid = true;
if (m_projectNameLineEdit->text().isEmpty())
if (m_projectName->lineEdit()->text().isEmpty())
{
projectNameIsValid = false;
}
bool projectPathIsValid = true;
if (m_projectPathLineEdit->text().isEmpty())
if (m_projectPath->lineEdit()->text().isEmpty())
{
projectPathIsValid = false;
}
QDir path(QDir::toNativeSeparators(m_projectPathLineEdit->text() + "/" + m_projectNameLineEdit->text()));
QDir path(QDir::toNativeSeparators(m_projectPath->lineEdit()->text() + "/" + m_projectName->lineEdit()->text()));
if (path.exists() && !path.isEmpty())
{
projectPathIsValid = false;
@@ -17,10 +17,12 @@
#endif
QT_FORWARD_DECLARE_CLASS(QButtonGroup)
QT_FORWARD_DECLARE_CLASS(QLineEdit)
namespace O3DE::ProjectManager
{
QT_FORWARD_DECLARE_CLASS(FormLineEditWidget)
QT_FORWARD_DECLARE_CLASS(FormBrowseEditWidget)
class NewProjectSettingsScreen
: public ScreenWidget
{
@@ -38,8 +40,8 @@ namespace O3DE::ProjectManager
void HandleBrowseButton();
private:
QLineEdit* m_projectNameLineEdit;
QLineEdit* m_projectPathLineEdit;
FormLineEditWidget* m_projectName;
FormBrowseEditWidget* m_projectPath;
QButtonGroup* m_projectTemplateButtonGroup;
};
@@ -31,6 +31,7 @@ namespace O3DE::ProjectManager
LabelButton::LabelButton(QWidget* parent)
: QLabel(parent)
{
setObjectName("labelButton");
m_overlayLabel = new QLabel("", this);
m_overlayLabel->setObjectName("labelButtonOverlay");
m_overlayLabel->setWordWrap(true);
@@ -75,6 +76,8 @@ namespace O3DE::ProjectManager
void ProjectButton::Setup()
{
setObjectName("projectButton");
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setSpacing(0);
vLayout->setContentsMargins(0, 0, 0, 0);
@@ -98,14 +101,21 @@ namespace O3DE::ProjectManager
m_deleteProjectAction = newProjectMenu->addAction(tr("Delete the Project"));
#endif
m_projectSettingsMenuButton = new QPushButton(this);
m_projectSettingsMenuButton->setText(m_projectName);
m_projectSettingsMenuButton->setMenu(newProjectMenu);
m_projectSettingsMenuButton->setFocusPolicy(Qt::FocusPolicy::NoFocus);
m_projectSettingsMenuButton->setStyleSheet("font-size: 14px; text-align:left;");
vLayout->addWidget(m_projectSettingsMenuButton);
QFrame* footer = new QFrame(this);
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setContentsMargins(0, 0, 0, 0);
footer->setLayout(hLayout);
{
QLabel* projectNameLabel = new QLabel(m_projectName, this);
hLayout->addWidget(projectNameLabel);
setFixedSize(s_projectImageWidth, s_projectImageHeight + m_projectSettingsMenuButton->height());
QPushButton* projectMenuButton = new QPushButton(this);
projectMenuButton->setObjectName("projectMenuButton");
projectMenuButton->setMenu(newProjectMenu);
hLayout->addWidget(projectMenuButton);
}
vLayout->addWidget(footer);
connect(m_projectImageLabel, &LabelButton::triggered, [this]() { emit OpenProject(m_projectName); });
connect(m_editProjectAction, &QAction::triggered, [this]() { emit EditProject(m_projectName); });
@@ -73,7 +73,6 @@ namespace O3DE::ProjectManager
QString m_projectName;
QString m_projectImagePath;
LabelButton* m_projectImageLabel;
QPushButton* m_projectSettingsMenuButton;
QAction* m_editProjectAction;
QAction* m_editProjectGemsAction;
QAction* m_copyProjectAction;
@@ -11,52 +11,46 @@
*/
#include <ProjectManagerWindow.h>
#include <ScreenFactory.h>
#include <ScreensCtrl.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzCore/IO/Path/Path.h>
#include <QDir>
#include <Source/ui_ProjectManagerWindow.h>
namespace O3DE::ProjectManager
{
ProjectManagerWindow::ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath)
: QMainWindow(parent)
, m_ui(new Ui::ProjectManagerWindowClass())
{
m_ui->setupUi(this);
QLayout* layout = m_ui->centralWidget->layout();
layout->setMargin(0);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
m_pythonBindings = AZStd::make_unique<PythonBindings>(engineRootPath);
m_screensCtrl = new ScreensCtrl();
m_ui->verticalLayout->addWidget(m_screensCtrl);
setWindowTitle(tr("O3DE Project Manager"));
connect(m_ui->projectsMenu, &QMenu::aboutToShow, this, &ProjectManagerWindow::HandleProjectsMenu);
connect(m_ui->engineMenu, &QMenu::aboutToShow, this, &ProjectManagerWindow::HandleEngineMenu);
ScreensCtrl* screensCtrl = new ScreensCtrl();
// currently the tab order on the home page is based on the order of this list
QVector<ProjectManagerScreen> screenEnums =
{
ProjectManagerScreen::Projects,
ProjectManagerScreen::EngineSettings,
ProjectManagerScreen::CreateProject,
ProjectManagerScreen::UpdateProject
};
screensCtrl->BuildScreens(screenEnums);
setCentralWidget(screensCtrl);
// setup stylesheets and hot reloading
QDir rootDir = QString::fromUtf8(engineRootPath.Native().data(), aznumeric_cast<int>(engineRootPath.Native().size()));
const auto pathOnDisk = rootDir.absoluteFilePath("Code/Tools/ProjectManager/Resources");
const auto qrcPath = QStringLiteral(":/ProjectManager/style");
AzQtComponents::StyleManager::addSearchPaths("style", pathOnDisk, qrcPath, engineRootPath);
// set stylesheet after creating the screens or their styles won't get updated
AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:ProjectManager.qss"));
QVector<ProjectManagerScreen> screenEnums =
{
ProjectManagerScreen::FirstTimeUse,
ProjectManagerScreen::CreateProject,
ProjectManagerScreen::ProjectsHome,
ProjectManagerScreen::UpdateProject,
ProjectManagerScreen::EngineSettings
};
m_screensCtrl->BuildScreens(screenEnums);
m_screensCtrl->ForceChangeToScreen(ProjectManagerScreen::FirstTimeUse, false);
screensCtrl->ForceChangeToScreen(ProjectManagerScreen::Projects, false);
}
ProjectManagerWindow::~ProjectManagerWindow()
@@ -64,13 +58,4 @@ namespace O3DE::ProjectManager
m_pythonBindings.reset();
}
void ProjectManagerWindow::HandleProjectsMenu()
{
m_screensCtrl->ChangeToScreen(ProjectManagerScreen::ProjectsHome);
}
void ProjectManagerWindow::HandleEngineMenu()
{
m_screensCtrl->ChangeToScreen(ProjectManagerScreen::EngineSettings);
}
} // namespace O3DE::ProjectManager
@@ -13,17 +13,9 @@
#if !defined(Q_MOC_RUN)
#include <QMainWindow>
#include <ScreensCtrl.h>
#include <PythonBindings.h>
#endif
namespace Ui
{
class ProjectManagerWindowClass;
}
namespace O3DE::ProjectManager
{
class ProjectManagerWindow
@@ -35,13 +27,7 @@ namespace O3DE::ProjectManager
explicit ProjectManagerWindow(QWidget* parent, const AZ::IO::PathView& engineRootPath);
~ProjectManagerWindow();
protected slots:
void HandleProjectsMenu();
void HandleEngineMenu();
private:
QScopedPointer<Ui::ProjectManagerWindowClass> m_ui;
ScreensCtrl* m_screensCtrl;
AZStd::unique_ptr<PythonBindings> m_pythonBindings;
};
@@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProjectManagerWindowClass</class>
<widget class="QMainWindow" name="ProjectManagerWindowClass">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>800</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>O3DE Project Manager</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout"/>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>36</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<widget class="QMenu" name="iconMenu">
<property name="title">
<string>Icon</string>
</property>
<property name="icon">
<iconset resource="../Resources/ProjectManager.qrc">
<normaloff>:/o3de_editor.ico</normaloff>:/o3de_editor.ico</iconset>
</property>
</widget>
<widget class="QMenu" name="projectsMenu">
<property name="title">
<string>Projects</string>
</property>
</widget>
<widget class="QMenu" name="engineMenu">
<property name="title">
<string>Engine</string>
</property>
</widget>
<addaction name="iconMenu"/>
<addaction name="projectsMenu"/>
<addaction name="engineMenu"/>
</widget>
</widget>
<resources>
<include location="../Resources/ProjectManager.qrc"/>
</resources>
<connections/>
</ui>
@@ -1,206 +0,0 @@
/*
* 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 <ProjectsHomeScreen.h>
#include <ProjectButtonWidget.h>
#include <PythonBindingsInterface.h>
#include <AzCore/Platform.h>
#include <AzCore/IO/SystemFile.h>
#include <AzFramework/AzFramework_Traits_Platform.h>
#include <AzFramework/Process/ProcessCommon.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzCore/Utils/Utils.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QListView>
#include <QSpacerItem>
#include <QListWidget>
#include <QListWidgetItem>
#include <QFileInfo>
#include <QScrollArea>
#include <QMessageBox>
#include <QTimer>
namespace O3DE::ProjectManager
{
ProjectsHomeScreen::ProjectsHomeScreen(QWidget* parent)
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
setLayout(vLayout);
vLayout->setContentsMargins(s_contentMargins, s_contentMargins, s_contentMargins, s_contentMargins);
QHBoxLayout* topLayout = new QHBoxLayout();
QLabel* titleLabel = new QLabel(this);
titleLabel->setText("My Projects");
titleLabel->setStyleSheet("font-size: 24px");
topLayout->addWidget(titleLabel);
QSpacerItem* topSpacer = new QSpacerItem(s_spacerSize, s_spacerSize, QSizePolicy::Expanding, QSizePolicy::Minimum);
topLayout->addItem(topSpacer);
QMenu* newProjectMenu = new QMenu(this);
m_createNewProjectAction = newProjectMenu->addAction("Create New Project");
m_addExistingProjectAction = newProjectMenu->addAction("Add Existing Project");
QPushButton* newProjectMenuButton = new QPushButton(this);
newProjectMenuButton->setText("New Project...");
newProjectMenuButton->setMenu(newProjectMenu);
newProjectMenuButton->setFixedWidth(s_newProjectButtonWidth);
newProjectMenuButton->setStyleSheet("font-size: 14px;");
topLayout->addWidget(newProjectMenuButton);
vLayout->addLayout(topLayout);
// Get all projects and create a horizontal scrolling list of them
auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty())
{
QScrollArea* projectsScrollArea = new QScrollArea(this);
QWidget* scrollWidget = new QWidget();
QGridLayout* projectGridLayout = new QGridLayout();
scrollWidget->setLayout(projectGridLayout);
projectsScrollArea->setWidget(scrollWidget);
projectsScrollArea->setWidgetResizable(true);
int gridIndex = 0;
for (auto project : projectsResult.GetValue())
{
ProjectButton* projectButton;
QString projectPreviewPath = project.m_path + m_projectPreviewImagePath;
QFileInfo doesPreviewExist(projectPreviewPath);
if (doesPreviewExist.exists() && doesPreviewExist.isFile())
{
projectButton = new ProjectButton(project.m_projectName, projectPreviewPath, this);
}
else
{
projectButton = new ProjectButton(project.m_projectName, this);
}
// Create rows of projects buttons s_projectButtonRowCount buttons wide
projectGridLayout->addWidget(projectButton, gridIndex / s_projectButtonRowCount, gridIndex % s_projectButtonRowCount);
connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsHomeScreen::HandleOpenProject);
connect(projectButton, &ProjectButton::EditProject, this, &ProjectsHomeScreen::HandleEditProject);
#ifdef SHOW_ALL_PROJECT_ACTIONS
connect(projectButton, &ProjectButton::EditProjectGems, this, &ProjectsHomeScreen::HandleEditProjectGems);
connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsHomeScreen::HandleCopyProject);
connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsHomeScreen::HandleRemoveProject);
connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsHomeScreen::HandleDeleteProject);
#endif
++gridIndex;
}
vLayout->addWidget(projectsScrollArea);
}
// Using border-image allows for scaling options background-image does not support
setStyleSheet("O3DE--ProjectManager--ScreenWidget { border-image: url(:/Backgrounds/FirstTimeBackgroundImage.jpg) repeat repeat; }");
connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsHomeScreen::HandleNewProjectButton);
connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsHomeScreen::HandleAddProjectButton);
}
ProjectManagerScreen ProjectsHomeScreen::GetScreenEnum()
{
return ProjectManagerScreen::ProjectsHome;
}
void ProjectsHomeScreen::HandleNewProjectButton()
{
emit ResetScreenRequest(ProjectManagerScreen::CreateProject);
emit ChangeScreenRequest(ProjectManagerScreen::CreateProject);
}
void ProjectsHomeScreen::HandleAddProjectButton()
{
// Do nothing for now
}
void ProjectsHomeScreen::HandleOpenProject(const QString& projectPath)
{
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());
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)
{
button->SetButtonEnabled(false);
button->SetButtonOverlayText(tr("Opening Editor..."));
}
// enable the button after 3 seconds
constexpr int waitTimeInMs = 3000;
QTimer::singleShot(waitTimeInMs, this, [this, button] {
if (button)
{
button->SetButtonEnabled(true);
}
});
}
}
else
{
AZ_Error("ProjectManager", false, "Cannot open editor because an empty project path was provided");
QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor because the project path is invalid."));
}
}
void ProjectsHomeScreen::HandleEditProject(const QString& projectPath)
{
emit NotifyCurrentProject(projectPath);
emit ResetScreenRequest(ProjectManagerScreen::UpdateProject);
emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject);
}
void ProjectsHomeScreen::HandleEditProjectGems(const QString& projectPath)
{
emit NotifyCurrentProject(projectPath);
emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog);
}
void ProjectsHomeScreen::HandleCopyProject([[maybe_unused]] const QString& projectPath)
{
// Open file dialog and choose location for copied project then register copy with O3DE
}
void ProjectsHomeScreen::HandleRemoveProject([[maybe_unused]] const QString& projectPath)
{
// Unregister Project from O3DE
}
void ProjectsHomeScreen::HandleDeleteProject([[maybe_unused]] const QString& projectPath)
{
// Remove project from 03DE and delete from disk
ProjectsHomeScreen::HandleRemoveProject(projectPath);
}
} // namespace O3DE::ProjectManager
@@ -0,0 +1,347 @@
/*
* 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 <ProjectsScreen.h>
#include <ProjectButtonWidget.h>
#include <PythonBindingsInterface.h>
#include <AzQtComponents/Components/FlowLayout.h>
#include <AzCore/Platform.h>
#include <AzCore/IO/SystemFile.h>
#include <AzFramework/AzFramework_Traits_Platform.h>
#include <AzFramework/Process/ProcessCommon.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzCore/Utils/Utils.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QListView>
#include <QSpacerItem>
#include <QListWidget>
#include <QListWidgetItem>
#include <QFileInfo>
#include <QScrollArea>
#include <QStackedWidget>
#include <QFrame>
#include <QIcon>
#include <QPixmap>
#include <QSettings>
#include <QMessageBox>
#include <QTimer>
//#define DISPLAY_PROJECT_DEV_DATA true
namespace O3DE::ProjectManager
{
ProjectsScreen::ProjectsScreen(QWidget* parent)
: ScreenWidget(parent)
{
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setAlignment(Qt::AlignTop);
vLayout->setContentsMargins(s_contentMargins, 0, s_contentMargins, 0);
setLayout(vLayout);
m_background.load(":/Backgrounds/FirstTimeBackgroundImage.jpg");
m_stack = new QStackedWidget(this);
m_firstTimeContent = CreateFirstTimeContent();
m_stack->addWidget(m_firstTimeContent);
m_projectsContent = CreateProjectsContent();
m_stack->addWidget(m_projectsContent);
vLayout->addWidget(m_stack);
connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleNewProjectButton);
connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleAddProjectButton);
}
QFrame* ProjectsScreen::CreateFirstTimeContent()
{
QFrame* frame = new QFrame(this);
frame->setObjectName("firstTimeContent");
{
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setAlignment(Qt::AlignTop);
frame->setLayout(layout);
QLabel* titleLabel = new QLabel(tr("Ready. Set. Create."), this);
titleLabel->setObjectName("titleLabel");
layout->addWidget(titleLabel);
QLabel* introLabel = new QLabel(this);
introLabel->setObjectName("introLabel");
introLabel->setText(tr("Welcome to O3DE! Start something new by creating a project. Not sure what to create? \nExplore what's "
"available by downloading our sample project."));
layout->addWidget(introLabel);
QHBoxLayout* buttonLayout = new QHBoxLayout(this);
buttonLayout->setAlignment(Qt::AlignLeft);
buttonLayout->setSpacing(s_spacerSize);
// use a newline to force the text up
QPushButton* createProjectButton = new QPushButton(tr("Create a Project\n"), this);
createProjectButton->setObjectName("createProjectButton");
buttonLayout->addWidget(createProjectButton);
QPushButton* addProjectButton = new QPushButton(tr("Add a Project\n"), this);
addProjectButton->setObjectName("addProjectButton");
buttonLayout->addWidget(addProjectButton);
connect(createProjectButton, &QPushButton::clicked, this, &ProjectsScreen::HandleNewProjectButton);
connect(addProjectButton, &QPushButton::clicked, this, &ProjectsScreen::HandleAddProjectButton);
layout->addLayout(buttonLayout);
}
return frame;
}
QFrame* ProjectsScreen::CreateProjectsContent()
{
QFrame* frame = new QFrame(this);
frame->setObjectName("projectsContent");
{
QVBoxLayout* layout = new QVBoxLayout();
layout->setAlignment(Qt::AlignTop);
layout->setContentsMargins(0, 0, 0, 0);
frame->setLayout(layout);
QFrame* header = new QFrame(this);
QHBoxLayout* headerLayout = new QHBoxLayout();
{
QLabel* titleLabel = new QLabel(tr("My Projects"), this);
titleLabel->setObjectName("titleLabel");
headerLayout->addWidget(titleLabel);
QMenu* newProjectMenu = new QMenu(this);
m_createNewProjectAction = newProjectMenu->addAction("Create New Project");
m_addExistingProjectAction = newProjectMenu->addAction("Add Existing Project");
connect(m_createNewProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleNewProjectButton);
connect(m_addExistingProjectAction, &QAction::triggered, this, &ProjectsScreen::HandleAddProjectButton);
QPushButton* newProjectMenuButton = new QPushButton(tr("New Project..."), this);
newProjectMenuButton->setObjectName("newProjectButton");
newProjectMenuButton->setMenu(newProjectMenu);
newProjectMenuButton->setDefault(true);
headerLayout->addWidget(newProjectMenuButton);
}
header->setLayout(headerLayout);
layout->addWidget(header);
// Get all projects and create a horizontal scrolling list of them
auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
if (projectsResult.IsSuccess() && !projectsResult.GetValue().isEmpty())
{
QScrollArea* projectsScrollArea = new QScrollArea(this);
QWidget* scrollWidget = new QWidget();
FlowLayout* flowLayout = new FlowLayout(0, s_spacerSize, s_spacerSize);
scrollWidget->setLayout(flowLayout);
projectsScrollArea->setWidget(scrollWidget);
projectsScrollArea->setWidgetResizable(true);
#ifndef DISPLAY_PROJECT_DEV_DATA
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())
{
projectButton = new ProjectButton(project.m_projectName, projectPreviewPath, this);
}
else
{
projectButton = new ProjectButton(project.m_projectName, this);
}
flowLayout->addWidget(projectButton);
connect(projectButton, &ProjectButton::OpenProject, this, &ProjectsScreen::HandleOpenProject);
connect(projectButton, &ProjectButton::EditProject, this, &ProjectsScreen::HandleEditProject);
#ifdef DISPLAY_PROJECT_DEV_DATA
connect(projectButton, &ProjectButton::EditProjectGems, this, &ProjectsScreen::HandleEditProjectGems);
connect(projectButton, &ProjectButton::CopyProject, this, &ProjectsScreen::HandleCopyProject);
connect(projectButton, &ProjectButton::RemoveProject, this, &ProjectsScreen::HandleRemoveProject);
connect(projectButton, &ProjectButton::DeleteProject, this, &ProjectsScreen::HandleDeleteProject);
#endif
}
layout->addWidget(projectsScrollArea);
}
}
return frame;
}
ProjectManagerScreen ProjectsScreen::GetScreenEnum()
{
return ProjectManagerScreen::Projects;
}
bool ProjectsScreen::IsTab()
{
return true;
}
QString ProjectsScreen::GetTabText()
{
return tr("Projects");
}
void ProjectsScreen::paintEvent([[maybe_unused]] QPaintEvent* event)
{
// we paint the background here because qss does not support background cover scaling
QPainter painter(this);
auto winSize = size();
auto pixmapRatio = (float)m_background.width() / m_background.height();
auto windowRatio = (float)winSize.width() / winSize.height();
if (pixmapRatio > windowRatio)
{
auto newWidth = (int)(winSize.height() * pixmapRatio);
auto offset = (newWidth - winSize.width()) / -2;
painter.drawPixmap(offset, 0, newWidth, winSize.height(), m_background);
}
else
{
auto newHeight = (int)(winSize.width() / pixmapRatio);
painter.drawPixmap(0, 0, winSize.width(), newHeight, m_background);
}
}
void ProjectsScreen::HandleNewProjectButton()
{
emit ResetScreenRequest(ProjectManagerScreen::CreateProject);
emit ChangeScreenRequest(ProjectManagerScreen::CreateProject);
}
void ProjectsScreen::HandleAddProjectButton()
{
// Do nothing for now
}
void ProjectsScreen::HandleOpenProject(const QString& projectPath)
{
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());
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)
{
button->SetButtonEnabled(false);
button->SetButtonOverlayText(tr("Opening Editor..."));
}
// enable the button after 3 seconds
constexpr int waitTimeInMs = 3000;
QTimer::singleShot(waitTimeInMs, this, [this, button] {
if (button)
{
button->SetButtonEnabled(true);
}
});
}
}
else
{
AZ_Error("ProjectManager", false, "Cannot open editor because an empty project path was provided");
QMessageBox::critical( this, tr("Error"), tr("Failed to launch the Editor because the project path is invalid."));
}
}
void ProjectsScreen::HandleEditProject(const QString& projectPath)
{
emit NotifyCurrentProject(projectPath);
emit ResetScreenRequest(ProjectManagerScreen::UpdateProject);
emit ChangeScreenRequest(ProjectManagerScreen::UpdateProject);
}
void ProjectsScreen::HandleEditProjectGems(const QString& projectPath)
{
emit NotifyCurrentProject(projectPath);
emit ChangeScreenRequest(ProjectManagerScreen::GemCatalog);
}
void ProjectsScreen::HandleCopyProject([[maybe_unused]] const QString& projectPath)
{
// Open file dialog and choose location for copied project then register copy with O3DE
}
void ProjectsScreen::HandleRemoveProject([[maybe_unused]] const QString& projectPath)
{
// Unregister Project from O3DE
}
void ProjectsScreen::HandleDeleteProject([[maybe_unused]] const QString& projectPath)
{
// Remove project from 03DE and delete from disk
ProjectsScreen::HandleRemoveProject(projectPath);
}
void ProjectsScreen::NotifyCurrentScreen()
{
if (ShouldDisplayFirstTimeContent())
{
m_stack->setCurrentWidget(m_firstTimeContent);
}
else
{
m_stack->setCurrentWidget(m_projectsContent);
}
}
bool ProjectsScreen::ShouldDisplayFirstTimeContent()
{
auto projectsResult = PythonBindingsInterface::Get()->GetProjects();
if (!projectsResult.IsSuccess() || projectsResult.GetValue().isEmpty())
{
return true;
}
QSettings settings;
bool displayFirstTimeContent = settings.value("displayFirstTimeContent", true).toBool();
if (displayFirstTimeContent)
{
settings.setValue("displayFirstTimeContent", false);
}
return displayFirstTimeContent;
}
} // namespace O3DE::ProjectManager
@@ -15,16 +15,26 @@
#include <ScreenWidget.h>
#endif
QT_FORWARD_DECLARE_CLASS(QPaintEvent)
QT_FORWARD_DECLARE_CLASS(QFrame)
QT_FORWARD_DECLARE_CLASS(QStackedWidget)
namespace O3DE::ProjectManager
{
class ProjectsHomeScreen
class ProjectsScreen
: public ScreenWidget
{
public:
explicit ProjectsHomeScreen(QWidget* parent = nullptr);
~ProjectsHomeScreen() = default;
explicit ProjectsScreen(QWidget* parent = nullptr);
~ProjectsScreen() = default;
ProjectManagerScreen GetScreenEnum() override;
QString GetTabText() override;
bool IsTab() override;
protected:
void NotifyCurrentScreen() override;
protected slots:
void HandleNewProjectButton();
@@ -36,16 +46,24 @@ namespace O3DE::ProjectManager
void HandleRemoveProject(const QString& projectPath);
void HandleDeleteProject(const QString& projectPath);
void paintEvent(QPaintEvent* event) override;
private:
QFrame* CreateFirstTimeContent();
QFrame* CreateProjectsContent();
bool ShouldDisplayFirstTimeContent();
QAction* m_createNewProjectAction;
QAction* m_addExistingProjectAction;
QPixmap m_background;
QFrame* m_firstTimeContent;
QFrame* m_projectsContent;
QStackedWidget* m_stack;
const QString m_projectPreviewImagePath = "/preview.png";
inline constexpr static int s_contentMargins = 80;
inline constexpr static int s_spacerSize = 20;
inline constexpr static int s_projectButtonRowCount = 4;
inline constexpr static int s_newProjectButtonWidth = 156;
};
} // namespace O3DE::ProjectManager
@@ -17,11 +17,10 @@ namespace O3DE::ProjectManager
{
Invalid = -1,
Empty,
FirstTimeUse,
CreateProject,
NewProjectSettings,
GemCatalog,
ProjectsHome,
Projects,
UpdateProject,
ProjectSettings,
EngineSettings
@@ -11,12 +11,11 @@
*/
#include <ScreenFactory.h>
#include <FirstTimeUseScreen.h>
#include <CreateProjectCtrl.h>
#include <UpdateProjectCtrl.h>
#include <NewProjectSettingsScreen.h>
#include <GemCatalog/GemCatalogScreen.h>
#include <ProjectsHomeScreen.h>
#include <ProjectsScreen.h>
#include <ProjectSettingsScreen.h>
#include <EngineSettingsScreen.h>
@@ -28,9 +27,6 @@ namespace O3DE::ProjectManager
switch(screen)
{
case (ProjectManagerScreen::FirstTimeUse):
newScreen = new FirstTimeUseScreen(parent);
break;
case (ProjectManagerScreen::CreateProject):
newScreen = new CreateProjectCtrl(parent);
break;
@@ -40,8 +36,8 @@ namespace O3DE::ProjectManager
case (ProjectManagerScreen::GemCatalog):
newScreen = new GemCatalogScreen(parent);
break;
case (ProjectManagerScreen::ProjectsHome):
newScreen = new ProjectsHomeScreen(parent);
case (ProjectManagerScreen::Projects):
newScreen = new ProjectsScreen(parent);
break;
case (ProjectManagerScreen::UpdateProject):
newScreen = new UpdateProjectCtrl(parent);
@@ -0,0 +1,62 @@
/*
* 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 <ScreenHeaderWidget.h>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
namespace O3DE::ProjectManager
{
ScreenHeader::ScreenHeader(QWidget* parent)
: QFrame(parent)
{
setObjectName("header");
QHBoxLayout* layout = new QHBoxLayout();
layout->setAlignment(Qt::AlignLeft);
layout->setContentsMargins(0,0,0,0);
m_backButton = new QPushButton();
m_backButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
layout->addWidget(m_backButton);
QVBoxLayout* titleLayout = new QVBoxLayout();
m_title = new QLabel();
m_title->setObjectName("headerTitle");
titleLayout->addWidget(m_title);
m_subTitle = new QLabel();
m_subTitle->setObjectName("headerSubTitle");
titleLayout->addWidget(m_subTitle);
layout->addLayout(titleLayout);
setLayout(layout);
}
void ScreenHeader::setTitle(const QString& text)
{
m_title->setText(text);
}
void ScreenHeader::setSubTitle(const QString& text)
{
m_subTitle->setText(text);
}
QPushButton* ScreenHeader::backButton()
{
return m_backButton;
}
} // namespace O3DE::ProjectManager
@@ -0,0 +1,42 @@
/*
* 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 <QFrame>
#endif
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QPushButton)
namespace O3DE::ProjectManager
{
class ScreenHeader
: public QFrame
{
Q_OBJECT // AUTOMOC
public:
ScreenHeader(QWidget* parent = nullptr);
void setTitle(const QString& text);
void setSubTitle(const QString& text);
QPushButton* backButton();
private:
QLabel* m_title;
QLabel* m_subTitle;
QPushButton* m_backButton;
};
} // namespace O3DE::ProjectManager
@@ -41,12 +41,27 @@ namespace O3DE::ProjectManager
{
return true;
}
virtual bool IsTab()
{
return false;
}
virtual QString GetTabText()
{
return tr("Missing");
}
//! Notify this screen it is the current screen
virtual void NotifyCurrentScreen()
{
}
signals:
void ChangeScreenRequest(ProjectManagerScreen screen);
void GotoPreviousScreenRequest();
void ResetScreenRequest(ProjectManagerScreen screen);
void NotifyCurrentProject(const QString& projectPath);
};
} // namespace O3DE::ProjectManager
@@ -14,6 +14,7 @@
#include <ScreenFactory.h>
#include <ScreenWidget.h>
#include <QTabWidget>
#include <QVBoxLayout>
namespace O3DE::ProjectManager
@@ -21,17 +22,19 @@ namespace O3DE::ProjectManager
ScreensCtrl::ScreensCtrl(QWidget* parent)
: QWidget(parent)
{
setObjectName("ScreensCtrl");
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->setMargin(0);
vLayout->setSpacing(0);
vLayout->setContentsMargins(0, 0, 0, 0);
setLayout(vLayout);
m_screenStack = new QStackedWidget();
vLayout->addWidget(m_screenStack);
//Track the bottom of the stack
m_screenVisitOrder.push(ProjectManagerScreen::Invalid);
// add a tab widget at the bottom of the stack
m_tabWidget = new QTabWidget();
m_screenStack->addWidget(m_tabWidget);
connect(m_tabWidget, &QTabWidget::currentChanged, this, &ScreensCtrl::TabChanged);
}
void ScreensCtrl::BuildScreens(QVector<ProjectManagerScreen> screens)
@@ -57,7 +60,14 @@ namespace O3DE::ProjectManager
ScreenWidget* ScreensCtrl::GetCurrentScreen()
{
return reinterpret_cast<ScreenWidget*>(m_screenStack->currentWidget());
if (m_screenStack->currentWidget() == m_tabWidget)
{
return reinterpret_cast<ScreenWidget*>(m_tabWidget->currentWidget());
}
else
{
return reinterpret_cast<ScreenWidget*>(m_screenStack->currentWidget());
}
}
bool ScreensCtrl::ChangeToScreen(ProjectManagerScreen screen)
@@ -79,13 +89,28 @@ namespace O3DE::ProjectManager
if (iterator != m_screenMap.end())
{
ScreenWidget* currentScreen = GetCurrentScreen();
if (currentScreen != iterator.value())
ScreenWidget* newScreen = iterator.value();
if (currentScreen != newScreen)
{
if (addVisit)
{
m_screenVisitOrder.push(currentScreen->GetScreenEnum());
ProjectManagerScreen oldScreen = currentScreen->GetScreenEnum();
m_screenVisitOrder.push(oldScreen);
}
m_screenStack->setCurrentWidget(iterator.value());
if (newScreen->IsTab())
{
m_tabWidget->setCurrentWidget(newScreen);
m_screenStack->setCurrentWidget(m_tabWidget);
}
else
{
m_screenStack->setCurrentWidget(newScreen);
}
newScreen->NotifyCurrentScreen();
return true;
}
}
@@ -95,23 +120,46 @@ namespace O3DE::ProjectManager
bool ScreensCtrl::GotoPreviousScreen()
{
// Don't go back if we are on the first set screen
if (m_screenVisitOrder.top() != ProjectManagerScreen::Invalid)
if (!m_screenVisitOrder.isEmpty())
{
// We do not check with screen if we can go back, we should always be able to go back
return ForceChangeToScreen(m_screenVisitOrder.pop(), false);
ProjectManagerScreen previousScreen = m_screenVisitOrder.pop();
return ForceChangeToScreen(previousScreen, false);
}
return false;
}
void ScreensCtrl::ResetScreen(ProjectManagerScreen screen)
{
bool shouldRestoreCurrentScreen = false;
if (GetCurrentScreen() && GetCurrentScreen()->GetScreenEnum() == screen)
{
shouldRestoreCurrentScreen = true;
}
// Delete old screen if it exists to start fresh
DeleteScreen(screen);
// Add new screen
ScreenWidget* newScreen = BuildScreen(this, screen);
m_screenStack->addWidget(newScreen);
if (newScreen->IsTab())
{
m_tabWidget->addTab(newScreen, newScreen->GetTabText());
if (shouldRestoreCurrentScreen)
{
m_tabWidget->setCurrentWidget(newScreen);
m_screenStack->setCurrentWidget(m_tabWidget);
}
}
else
{
m_screenStack->addWidget(newScreen);
if (shouldRestoreCurrentScreen)
{
m_screenStack->setCurrentWidget(newScreen);
}
}
m_screenMap.insert(screen, newScreen);
connect(newScreen, &ScreenWidget::ChangeScreenRequest, this, &ScreensCtrl::ChangeToScreen);
@@ -134,8 +182,21 @@ namespace O3DE::ProjectManager
const auto iter = m_screenMap.find(screen);
if (iter != m_screenMap.end())
{
m_screenStack->removeWidget(iter.value());
iter.value()->deleteLater();
ScreenWidget* screenToDelete = iter.value();
if (screenToDelete->IsTab())
{
int tabIndex = m_tabWidget->indexOf(screenToDelete);
if (tabIndex > -1)
{
m_tabWidget->removeTab(tabIndex);
}
}
else
{
// if the screen we delete is the current widget, a new one will
// be selected automatically (randomly?)
m_screenStack->removeWidget(screenToDelete);
}
// Erase does not cause a rehash so interators remain valid
m_screenMap.erase(iter);
@@ -150,4 +211,12 @@ namespace O3DE::ProjectManager
}
}
void ScreensCtrl::TabChanged([[maybe_unused]] int index)
{
ScreenWidget* screen = reinterpret_cast<ScreenWidget*>(m_tabWidget->currentWidget());
if (screen)
{
screen->NotifyCurrentScreen();
}
}
} // namespace O3DE::ProjectManager
@@ -18,6 +18,8 @@
#include <QStack>
#endif
QT_FORWARD_DECLARE_CLASS(QTabWidget)
namespace O3DE::ProjectManager
{
class ScreenWidget;
@@ -46,11 +48,13 @@ namespace O3DE::ProjectManager
void ResetAllScreens();
void DeleteScreen(ProjectManagerScreen screen);
void DeleteAllScreens();
void TabChanged(int index);
private:
QStackedWidget* m_screenStack;
QHash<ProjectManagerScreen, ScreenWidget*> m_screenMap;
QStack<ProjectManagerScreen> m_screenVisitOrder;
QTabWidget* m_tabWidget;
};
} // namespace O3DE::ProjectManager
@@ -108,7 +108,7 @@ namespace O3DE::ProjectManager
auto result = PythonBindingsInterface::Get()->UpdateProject(m_projectInfo);
if (result)
{
emit ChangeScreenRequest(ProjectManagerScreen::ProjectsHome);
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
}
else
{
+6 -1
View File
@@ -35,7 +35,6 @@ int main(int argc, char* argv[])
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
int runSuccess = 0;
{
@@ -55,6 +54,12 @@ int main(int argc, char* argv[])
O3DE::ProjectManager::ProjectManagerWindow window(nullptr, engineRootPath);
window.show();
// somethings is preventing us from moving the window to the center of the
// primary screen - likely an Az style or component helper
constexpr int width = 1200;
constexpr int height = 800;
window.resize(width, height);
runSuccess = app.exec();
}
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();