Improved project creation validation
No longer requires project name to be part of the project path.
This commit is contained in:
@@ -67,6 +67,15 @@ namespace O3DE::ProjectManager
|
||||
return ProjectManagerScreen::CreateProject;
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::NotifyCurrentScreen()
|
||||
{
|
||||
ScreenWidget* currentScreen = reinterpret_cast<ScreenWidget*>(m_stack->currentWidget());
|
||||
if (currentScreen)
|
||||
{
|
||||
currentScreen->NotifyCurrentScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void CreateProjectCtrl::HandleBackButton()
|
||||
{
|
||||
if (m_stack->currentIndex() > 0)
|
||||
@@ -110,6 +119,9 @@ namespace O3DE::ProjectManager
|
||||
auto result = PythonBindingsInterface::Get()->CreateProject(m_projectTemplatePath, m_projectInfo);
|
||||
if (result.IsSuccess())
|
||||
{
|
||||
// automatically register the project
|
||||
PythonBindingsInterface::Get()->AddProject(m_projectInfo.m_path);
|
||||
|
||||
// adding gems is not implemented yet because we don't know what targets to add or how to add them
|
||||
emit ChangeScreenRequest(ProjectManagerScreen::Projects);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace O3DE::ProjectManager
|
||||
explicit CreateProjectCtrl(QWidget* parent = nullptr);
|
||||
~CreateProjectCtrl() = default;
|
||||
ProjectManagerScreen GetScreenEnum() override;
|
||||
void NotifyCurrentScreen() override;
|
||||
|
||||
protected slots:
|
||||
void HandleBackButton();
|
||||
|
||||
@@ -78,6 +78,14 @@ namespace O3DE::ProjectManager
|
||||
m_errorLabel->setText(labelText);
|
||||
}
|
||||
|
||||
void FormLineEditWidget::setErrorLabelVisible(bool visible)
|
||||
{
|
||||
m_errorLabel->setVisible(visible);
|
||||
m_frame->setProperty("Valid", !visible);
|
||||
|
||||
refreshStyle();
|
||||
}
|
||||
|
||||
QLineEdit* FormLineEditWidget::lineEdit() const
|
||||
{
|
||||
return m_lineEdit;
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace O3DE::ProjectManager
|
||||
|
||||
//! Set the error message for to display when invalid.
|
||||
void setErrorLabelText(const QString& labelText);
|
||||
void setErrorLabelVisible(bool visible);
|
||||
|
||||
//! Returns a pointer to the underlying LineEdit.
|
||||
QLineEdit* lineEdit() const;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <FormLineEditWidget.h>
|
||||
#include <FormBrowseEditWidget.h>
|
||||
#include <PathValidator.h>
|
||||
#include <EngineInfo.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
@@ -49,16 +50,16 @@ namespace O3DE::ProjectManager
|
||||
vLayout->setContentsMargins(0,0,0,0);
|
||||
vLayout->setAlignment(Qt::AlignTop);
|
||||
{
|
||||
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."));
|
||||
const QString defaultName{ "NewProject" };
|
||||
const QString defaultPath = QDir::toNativeSeparators(GetDefaultProjectPath() + "/" + defaultName);
|
||||
|
||||
m_projectName = new FormLineEditWidget(tr("Project name"), defaultName, this);
|
||||
connect(m_projectName->lineEdit(), &QLineEdit::textChanged, this, &NewProjectSettingsScreen::ValidateProjectPath);
|
||||
vLayout->addWidget(m_projectName);
|
||||
|
||||
m_projectPath =
|
||||
new FormBrowseEditWidget(tr("Project Location"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), this);
|
||||
m_projectPath = new FormBrowseEditWidget(tr("Project Location"), defaultPath, 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));
|
||||
connect(m_projectPath->lineEdit(), &QLineEdit::textChanged, this, &NewProjectSettingsScreen::ValidateProjectPath);
|
||||
vLayout->addWidget(m_projectPath);
|
||||
|
||||
// if we don't use a QFrame we cannot "contain" the widgets inside and move them around
|
||||
@@ -112,17 +113,41 @@ namespace O3DE::ProjectManager
|
||||
this->setLayout(hLayout);
|
||||
}
|
||||
|
||||
QString NewProjectSettingsScreen::GetDefaultProjectPath()
|
||||
{
|
||||
QString defaultPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||
AZ::Outcome<EngineInfo> engineInfoResult = PythonBindingsInterface::Get()->GetEngineInfo();
|
||||
if (engineInfoResult.IsSuccess())
|
||||
{
|
||||
QDir path(QDir::toNativeSeparators(engineInfoResult.GetValue().m_defaultProjectsFolder));
|
||||
if (path.exists())
|
||||
{
|
||||
defaultPath = path.absolutePath();
|
||||
}
|
||||
}
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
ProjectManagerScreen NewProjectSettingsScreen::GetScreenEnum()
|
||||
{
|
||||
return ProjectManagerScreen::NewProjectSettings;
|
||||
}
|
||||
|
||||
void NewProjectSettingsScreen::ValidateProjectPath()
|
||||
{
|
||||
Validate();
|
||||
}
|
||||
|
||||
void NewProjectSettingsScreen::NotifyCurrentScreen()
|
||||
{
|
||||
Validate();
|
||||
}
|
||||
|
||||
ProjectInfo NewProjectSettingsScreen::GetProjectInfo()
|
||||
{
|
||||
ProjectInfo projectInfo;
|
||||
projectInfo.m_projectName = m_projectName->lineEdit()->text();
|
||||
projectInfo.m_path = QDir::toNativeSeparators(m_projectPath->lineEdit()->text() + "/" + projectInfo.m_projectName);
|
||||
projectInfo.m_path = m_projectPath->lineEdit()->text();
|
||||
return projectInfo;
|
||||
}
|
||||
|
||||
@@ -133,24 +158,44 @@ namespace O3DE::ProjectManager
|
||||
|
||||
bool NewProjectSettingsScreen::Validate()
|
||||
{
|
||||
bool projectNameIsValid = true;
|
||||
if (m_projectName->lineEdit()->text().isEmpty())
|
||||
{
|
||||
projectNameIsValid = false;
|
||||
}
|
||||
|
||||
bool projectPathIsValid = true;
|
||||
if (m_projectPath->lineEdit()->text().isEmpty())
|
||||
{
|
||||
projectPathIsValid = false;
|
||||
m_projectPath->setErrorLabelText(tr("Please provide a valid location."));
|
||||
}
|
||||
|
||||
QDir path(QDir::toNativeSeparators(m_projectPath->lineEdit()->text() + "/" + m_projectName->lineEdit()->text()));
|
||||
if (path.exists() && !path.isEmpty())
|
||||
else
|
||||
{
|
||||
projectPathIsValid = false;
|
||||
QDir path(m_projectPath->lineEdit()->text());
|
||||
if (path.exists() && !path.isEmpty())
|
||||
{
|
||||
projectPathIsValid = false;
|
||||
m_projectPath->setErrorLabelText(tr("This folder exists and isn't empty. Please choose a different location."));
|
||||
}
|
||||
}
|
||||
|
||||
bool projectNameIsValid = true;
|
||||
if (m_projectName->lineEdit()->text().isEmpty())
|
||||
{
|
||||
projectNameIsValid = false;
|
||||
m_projectName->setErrorLabelText(tr("Please provide a project name."));
|
||||
}
|
||||
else
|
||||
{
|
||||
// this validation should roughly match the utils.validate_identifier which the cli
|
||||
// uses to validate project names
|
||||
QRegExp validProjectNameRegex("[A-Za-z][A-Za-z0-9_-]{0,63}");
|
||||
const bool result = validProjectNameRegex.exactMatch(m_projectName->lineEdit()->text());
|
||||
if (!result)
|
||||
{
|
||||
projectNameIsValid = false;
|
||||
m_projectName->setErrorLabelText(tr("Project names must start with a letter and consist of up to 64 letter, number, '_' or '-' characters"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_projectName->setErrorLabelVisible(!projectNameIsValid);
|
||||
m_projectPath->setErrorLabelVisible(!projectPathIsValid);
|
||||
return projectNameIsValid && projectPathIsValid;
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -36,10 +36,15 @@ namespace O3DE::ProjectManager
|
||||
|
||||
bool Validate();
|
||||
|
||||
void NotifyCurrentScreen() override;
|
||||
|
||||
protected slots:
|
||||
void HandleBrowseButton();
|
||||
void ValidateProjectPath();
|
||||
|
||||
private:
|
||||
QString GetDefaultProjectPath();
|
||||
|
||||
FormLineEditWidget* m_projectName;
|
||||
FormBrowseEditWidget* m_projectPath;
|
||||
QButtonGroup* m_projectTemplateButtonGroup;
|
||||
|
||||
@@ -341,6 +341,16 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,10 +513,15 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
ProjectInfo createdProjectInfo;
|
||||
bool result = ExecuteWithLock([&] {
|
||||
|
||||
pybind11::str projectPath = projectInfo.m_path.toStdString();
|
||||
pybind11::str projectName = projectInfo.m_projectName.toStdString();
|
||||
pybind11::str templatePath = projectTemplatePath.toStdString();
|
||||
auto createProjectResult = m_engineTemplate.attr("create_project")(projectPath, templatePath);
|
||||
|
||||
auto createProjectResult = m_engineTemplate.attr("create_project")(
|
||||
projectPath,
|
||||
projectName,
|
||||
templatePath
|
||||
);
|
||||
if (createProjectResult.cast<int>() == 0)
|
||||
{
|
||||
createdProjectInfo = ProjectInfoFromPath(projectPath);
|
||||
|
||||
@@ -136,6 +136,7 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
shouldRestoreCurrentScreen = true;
|
||||
}
|
||||
int tabIndex = GetScreenTabIndex(screen);
|
||||
|
||||
// Delete old screen if it exists to start fresh
|
||||
DeleteScreen(screen);
|
||||
@@ -144,11 +145,19 @@ namespace O3DE::ProjectManager
|
||||
ScreenWidget* newScreen = BuildScreen(this, screen);
|
||||
if (newScreen->IsTab())
|
||||
{
|
||||
m_tabWidget->addTab(newScreen, newScreen->GetTabText());
|
||||
if (tabIndex > -1)
|
||||
{
|
||||
m_tabWidget->insertTab(tabIndex, newScreen, newScreen->GetTabText());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tabWidget->addTab(newScreen, newScreen->GetTabText());
|
||||
}
|
||||
if (shouldRestoreCurrentScreen)
|
||||
{
|
||||
m_tabWidget->setCurrentWidget(newScreen);
|
||||
m_screenStack->setCurrentWidget(m_tabWidget);
|
||||
newScreen->NotifyCurrentScreen();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -157,6 +166,7 @@ namespace O3DE::ProjectManager
|
||||
if (shouldRestoreCurrentScreen)
|
||||
{
|
||||
m_screenStack->setCurrentWidget(newScreen);
|
||||
newScreen->NotifyCurrentScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,4 +229,19 @@ namespace O3DE::ProjectManager
|
||||
screen->NotifyCurrentScreen();
|
||||
}
|
||||
}
|
||||
|
||||
int ScreensCtrl::GetScreenTabIndex(ProjectManagerScreen screen)
|
||||
{
|
||||
const auto iter = m_screenMap.find(screen);
|
||||
if (iter != m_screenMap.end())
|
||||
{
|
||||
ScreenWidget* screenWidget = iter.value();
|
||||
if (screenWidget->IsTab())
|
||||
{
|
||||
return m_tabWidget->indexOf(screenWidget);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -51,6 +51,8 @@ namespace O3DE::ProjectManager
|
||||
void TabChanged(int index);
|
||||
|
||||
private:
|
||||
int GetScreenTabIndex(ProjectManagerScreen screen);
|
||||
|
||||
QStackedWidget* m_screenStack;
|
||||
QHash<ProjectManagerScreen, ScreenWidget*> m_screenMap;
|
||||
QStack<ProjectManagerScreen> m_screenVisitOrder;
|
||||
|
||||
Reference in New Issue
Block a user