You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Editor/AssetImporter/UI/SelectDestinationDialog.cpp

252 lines
9.0 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "SelectDestinationDialog.h"
// Qt
#include <QValidator>
#include <QSettings>
#include <QStyle>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <AssetImporter/UI/ui_SelectDestinationDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
static const char* g_assetProcessorLink = "<a href=\"https://o3de.org/docs/user-guide/assets/pipeline/processor-ui/\">Asset Processor</a>";
static const char* g_copyFilesMessage = "The original file will remain outside of the project and the %1 will not monitor the file.";
static const char* g_moveFilesMessage = "The original file will be moved inside of the project and the %1 will monitor the file for changes.";
static const char* g_selectDestinationFilesPath = "AssetImporter/SelectDestinationFilesPath";
static const char* g_toolTipInvalidRoot = "<p style='white-space:pre'> <span style=\"color:#cccccc;\">Invalid directory. Please choose a destination directory within your game project: %1.</span> </p>";
static const char* g_toolTipPathMustExist = "<p style='white-space:pre'> <span style=\"color:#cccccc;\">Invalid directory. Please choose a destination directory that exists.</span> </p>";
static const char* g_toolTipPathMustBeDirectory = "<p style='white-space:pre'> <span style=\"color:#cccccc;\">Invalid directory. Please choose a valid destination directory.</span> </p>";
static const char* g_toolTipInvalidLength = "<p style='white-space:pre'> <span style=\"color:#cccccc;\">Invalid directory name length. Please choose a destination path that has fewer than %1 characters.</span> </p>";
namespace
{
static QString GetAbsoluteRootDirectoryPath()
{
QDir gameRoot(Path::GetEditingGameDataFolder().c_str());
return gameRoot.absolutePath();
}
}
class DestinationDialogValidator
: public QValidator
{
public:
DestinationDialogValidator(QObject* parent)
: QValidator(parent)
, m_gameRootAbsolutePath(GetAbsoluteRootDirectoryPath())
{
}
State validate(QString& input, [[maybe_unused]] int& pos) const override
{
m_toolTip = "";
if (input.isEmpty())
{
return QValidator::Acceptable;
}
// The underlying file system code can't cope with long file paths, regardless of platform
if (input.length() > (AZ_MAX_PATH_LEN - 1))
{
m_toolTip = QString(g_toolTipInvalidLength).arg(AZ_MAX_PATH_LEN);
return QValidator::Intermediate;
}
QString normalizedInput = QDir::fromNativeSeparators(input);
// Note: the check for the root directory is case insensitive.
// We check if the directory actually exists after this, and at that point,
// if the file path has to be case sensitive, the directory won't exist anyways
// and QFileInfo::exists() will tell us so this should still work even on
// case sensitive file systems (such as Mac and Linux)
if (!normalizedInput.startsWith(m_gameRootAbsolutePath, Qt::CaseInsensitive))
{
m_toolTip = QString(g_toolTipInvalidRoot).arg(m_gameRootAbsolutePath);
return QValidator::Intermediate;
}
QFileInfo fileInfo(normalizedInput);
if (!fileInfo.exists())
{
m_toolTip = g_toolTipPathMustExist;
return QValidator::Intermediate;
}
if (!fileInfo.isDir())
{
m_toolTip = g_toolTipPathMustBeDirectory;
return QValidator::Intermediate;
}
return QValidator::Acceptable;
}
QString infoToolTip() const
{
return m_toolTip;
}
private:
QString m_gameRootAbsolutePath;
mutable QString m_toolTip;
};
SelectDestinationDialog::SelectDestinationDialog(QString message, QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::SelectDestinationDialog)
, m_validator(new DestinationDialogValidator(this))
{
m_ui->setupUi(this);
QString radioButtonMessage = QString(g_copyFilesMessage).arg(g_assetProcessorLink);
m_ui->RaidoButtonMessage->setText(radioButtonMessage);
SetPreviousDestinationDirectory();
m_ui->DestinationLineEdit->setValidator(m_validator);
m_ui->DestinationLineEdit->setAlignment(Qt::AlignVCenter);
// Based on the current code structure, in order to prevent texts from overlapping the
// invalid icon, intentionally insert an empty icon at the end of the line edit field can do the trick.
m_ui->DestinationLineEdit->addAction(QIcon(""), QLineEdit::TrailingPosition);
connect(m_ui->DestinationLineEdit, &QLineEdit::textChanged, this, &SelectDestinationDialog::ValidatePath);
connect(m_ui->BrowseButton, &QPushButton::clicked, this, &SelectDestinationDialog::OnBrowseDestinationFilePath, Qt::UniqueConnection);
connect(m_ui->CopyFileRadioButton, &QRadioButton::toggled, this, &SelectDestinationDialog::ShowMessage);
UpdateMessage(message);
InitializeButtons();
}
SelectDestinationDialog::~SelectDestinationDialog()
{
}
void SelectDestinationDialog::InitializeButtons()
{
m_ui->CopyFileRadioButton->setChecked(true);
m_ui->buttonBox->setContentsMargins(0, 0, 16, 16);
QPushButton* importButton = m_ui->buttonBox->addButton(tr("Import"), QDialogButtonBox::AcceptRole);
QPushButton* cancelButton = m_ui->buttonBox->addButton(QDialogButtonBox::Cancel);
importButton->setProperty("class", "Primary");
importButton->setDefault(true);
cancelButton->setProperty("class", "AssetImporterButton");
cancelButton->style()->unpolish(cancelButton);
cancelButton->style()->polish(cancelButton);
cancelButton->update();
connect(importButton, &QPushButton::clicked, this, &SelectDestinationDialog::accept);
connect(cancelButton, &QPushButton::clicked, this, &SelectDestinationDialog::reject);
connect(this, &SelectDestinationDialog::UpdateImportButtonState, importButton, &QPushButton::setEnabled);
// To make sure the import button state is up to date
ValidatePath();
importButton->setAutoDefault(true);
}
void SelectDestinationDialog::SetPreviousDestinationDirectory()
{
QString gameRootAbsPath = GetAbsoluteRootDirectoryPath();
QSettings settings;
QString previousDestination = settings.value(g_selectDestinationFilesPath).toString();
// Case 1: if currentDestination is empty at this point, that means this is the first time
// users using the Asset Importer, set the default directory to be the current game project's root folder
// Case 2: if the current folder directory stored in the registry doesn't exist anymore,
// that means users have removed the directory already (deleted or use the Move feature).
// Case 3: if it's a directory outside of the game root folder, then in general,
// users have modified the folder directory in the registry. It should not be happening.
if (previousDestination.isEmpty() || !QDir(previousDestination).exists() || !previousDestination.startsWith(gameRootAbsPath, Qt::CaseInsensitive))
{
previousDestination = gameRootAbsPath;
}
m_ui->DestinationLineEdit->setText(QDir::toNativeSeparators(previousDestination));
}
void SelectDestinationDialog::accept()
{
QDialog::accept();
// This prevent users from not editing the destination line edit (manually type the directory or browse for the directory)
Q_EMIT SetDestinationDirectory(DestinationDirectory());
if (m_ui->CopyFileRadioButton->isChecked())
{
Q_EMIT DoCopyFiles();
}
else if (m_ui->MoveFileRadioButton->isChecked())
{
Q_EMIT DoMoveFiles();
}
}
void SelectDestinationDialog::reject()
{
Q_EMIT Cancel();
QDialog::reject();
}
void SelectDestinationDialog::ShowMessage()
{
QString message = m_ui->CopyFileRadioButton->isChecked() ? g_copyFilesMessage : g_moveFilesMessage;
m_ui->RaidoButtonMessage->setText(message.arg(g_assetProcessorLink));
}
void SelectDestinationDialog::OnBrowseDestinationFilePath()
{
Q_EMIT BrowseDestinationPath(m_ui->DestinationLineEdit);
}
void SelectDestinationDialog::UpdateMessage(QString message)
{
m_ui->NumberOfFilesMessage->setText(message);
}
void SelectDestinationDialog::ValidatePath()
{
if (!m_ui->DestinationLineEdit->hasAcceptableInput())
{
m_ui->DestinationLineEdit->setToolTip(m_validator->infoToolTip());
Q_EMIT UpdateImportButtonState(false);
}
else
{
QString destinationDirectory = DestinationDirectory();
int strLength = destinationDirectory.length();
// store the updated acceptable destination directory into the registry,
// so that when users manually modify the directory,
// the Asset Importer will remember it
Q_EMIT SetDestinationDirectory(destinationDirectory);
m_ui->DestinationLineEdit->setToolTip("");
Q_EMIT UpdateImportButtonState(strLength > 0);
}
}
QString SelectDestinationDialog::DestinationDirectory() const
{
return QDir::fromNativeSeparators(m_ui->DestinationLineEdit->text());
}
#include <AssetImporter/UI/moc_SelectDestinationDialog.cpp>