git mv Code\Sandbox\Editor Code/Editor

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-06-29 12:41:59 -07:00
parent 9f0bbf3b74
commit e34e36cb35
1415 changed files with 0 additions and 0 deletions
@@ -0,0 +1,214 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "AssetImporterDragAndDropHandler.h"
// Qt
#include <QMimeData>
// AzToolsFramework
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
// AzQtComponents
#include <AzQtComponents/DragAndDrop/MainWindowDragAndDrop.h>
// Editor
#include "AssetImporter/AssetImporterManager/AssetImporterManager.h"
bool AssetImporterDragAndDropHandler::m_dragAccepted = false;
AssetImporterDragAndDropHandler::AssetImporterDragAndDropHandler(QObject* parent, AssetImporterManager* const assetImporterManager)
: QObject(parent)
, m_assetImporterManager(assetImporterManager)
{
AzQtComponents::DragAndDropEventsBus::Handler::BusConnect(AzQtComponents::DragAndDropContexts::EditorMainWindow);
// They are used to prevent opening the Asset Importer by dragging and dropping files and folders to the Main Window when it is already running
connect(m_assetImporterManager, &AssetImporterManager::StartAssetImporter, this, &AssetImporterDragAndDropHandler::OnStartAssetImporter);
connect(m_assetImporterManager, &AssetImporterManager::StopAssetImporter, this, &AssetImporterDragAndDropHandler::OnStopAssetImporter);
}
AssetImporterDragAndDropHandler::~AssetImporterDragAndDropHandler()
{
AzQtComponents::DragAndDropEventsBus::Handler::BusDisconnect(AzQtComponents::DragAndDropContexts::EditorMainWindow);
}
void AssetImporterDragAndDropHandler::DragEnter(QDragEnterEvent* event, AzQtComponents::DragAndDropContextBase& /*context*/)
{
if (!m_isAssetImporterRunning)
{
ProcessDragEnter(event);
}
}
void AssetImporterDragAndDropHandler::Drop(QDropEvent* event, AzQtComponents::DragAndDropContextBase& /*context*/)
{
if (!m_dragAccepted)
{
return;
}
QStringList fileList = GetFileList(event);
if (!fileList.isEmpty())
{
Q_EMIT OpenAssetImporterManager(fileList);
}
// reset
m_dragAccepted = false;
}
void AssetImporterDragAndDropHandler::ProcessDragEnter(QDragEnterEvent* event)
{
m_dragAccepted = false;
const QMimeData* mimeData = event->mimeData();
// if the event hasn't been accepted already and the mimeData hasUrls()
if (event->isAccepted() || !mimeData->hasUrls())
{
return;
}
// prevent users from dragging and dropping files from the Asset Browser
if (mimeData->hasFormat(AzToolsFramework::AssetBrowser::AssetBrowserEntry::GetMimeType()))
{
return;
}
QList<QUrl> urlList = mimeData->urls();
int urlListSize = urlList.size();
// runs through the file list first and checks for any "crate" files - if it finds ANY, return (and don't accept the event)
for (int i = 0; i < urlListSize; ++i)
{
QUrl currentUrl = urlList.at(i);
if (currentUrl.isLocalFile())
{
QString path = urlList.at(i).toLocalFile();
if (ContainCrateFiles(path))
{
return;
}
}
}
for (int i = 0; i < urlListSize; ++i)
{
// Get the local file path
QString path = urlList.at(i).toLocalFile();
QDir dir(path);
QString relativePath = dir.relativeFilePath(path);
QString absPath = dir.absolutePath();
// check if the files/folders are under the game root directory
QDir gameRoot(Path::GetEditingGameDataFolder().c_str());
QString gameRootAbsPath = gameRoot.absolutePath();
if (absPath.startsWith(gameRootAbsPath, Qt::CaseInsensitive))
{
return;
}
QDirIterator it(absPath, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::Subdirectories);
QFileInfo info(absPath);
QString extension = info.completeSuffix();
// if it's not an empty folder directory or if it's a file,
// then allow the drag and drop process.
// Otherwise, prevent users from dragging and dropping empty folders
if (it.hasNext() || !extension.isEmpty())
{
// this is used in Drop()
m_dragAccepted = true;
}
}
// at this point, all files should be legal to be imported
// since they are not in the database
if (m_dragAccepted)
{
event->acceptProposedAction();
}
}
QStringList AssetImporterDragAndDropHandler::GetFileList(QDropEvent* event)
{
QStringList fileList;
const QMimeData* mimeData = event->mimeData();
QList<QUrl> urlList = mimeData->urls();
for (int i = 0; i < urlList.size(); ++i)
{
QUrl currentUrl = urlList.at(i);
if (currentUrl.isLocalFile())
{
QString path = urlList.at(i).toLocalFile();
if (!ContainCrateFiles(path))
{
fileList.append(path);
}
}
}
return fileList;
}
void AssetImporterDragAndDropHandler::OnStartAssetImporter()
{
m_isAssetImporterRunning = true;
}
void AssetImporterDragAndDropHandler::OnStopAssetImporter()
{
m_isAssetImporterRunning = false;
}
bool AssetImporterDragAndDropHandler::ContainCrateFiles(QString path)
{
QFileInfo fileInfo(path);
if (fileInfo.isFile())
{
return isCrateFile(fileInfo);
}
else
{
QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::Subdirectories);
while (it.hasNext())
{
QString str = it.next();
QFileInfo info(str);
if (isCrateFile(info))
{
return true;
}
}
}
return false;
}
bool AssetImporterDragAndDropHandler::isCrateFile(QFileInfo fileInfo)
{
return QStringLiteral("crate").compare(fileInfo.suffix(), Qt::CaseInsensitive) == 0;
}
#include <AssetImporter/AssetImporterManager/moc_AssetImporterDragAndDropHandler.cpp>
@@ -0,0 +1,68 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QObject>
#include <QFileInfo>
#include <QString>
#include <AzCore/EBus/EBus.h>
#include <AzQtComponents/Buses/DragAndDrop.h>
#include <AzToolsFramework/API/AssetDatabaseBus.h>
#include <AzCore/std/smart_ptr/scoped_ptr.h>
#endif
class MainWindow;
class AssetImporterManager;
namespace AzToolsFramework
{
namespace AssetDatabase
{
class AssetDatabaseConnection;
}
}
class AssetImporterDragAndDropHandler
: public QObject
, public AzQtComponents::DragAndDropEventsBus::Handler
{
Q_OBJECT
public:
explicit AssetImporterDragAndDropHandler(QObject* parent, AssetImporterManager* const assetImporterManager);
~AssetImporterDragAndDropHandler();
void DragEnter(QDragEnterEvent* event, AzQtComponents::DragAndDropContextBase& context) override;
void Drop(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context) override;
static void ProcessDragEnter(QDragEnterEvent* event);
static QStringList GetFileList(QDropEvent* event);
Q_SIGNALS:
void OpenAssetImporterManager(const QStringList& fileList);
public Q_SLOTS:
void OnStartAssetImporter();
void OnStopAssetImporter();
private:
bool m_isAssetImporterRunning = false;
AssetImporterManager* m_assetImporterManager;
static bool ContainCrateFiles(QString path);
static bool isCrateFile(QFileInfo fileInfo);
// it is used because MainWindow's dropEvent will ask the Ebus to call the Drop() function in AssetImporterDragAndDropHandler.
// That will cause the problem that even the crate objects are blocked by the DragEnter() in AssetImporterDragAndDropHandler,
// it will still open the Asset Importer
static bool m_dragAccepted;
};
@@ -0,0 +1,811 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "AssetImporterManager.h"
// Qt
#include <QFileDialog>
#include <QMessageBox>
#include <QSettings>
#include <QStandardPaths>
// AzFramework
#include <AzFramework/Asset/AssetSystemBus.h>
// AzToolsFramework
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
// Editor
#include "AssetImporter/UI/FilesAlreadyExistDialog.h"
#include "AssetImporter/UI/ProcessingAssetsDialog.h"
namespace AssetImporterManagerPrivate
{
const char* g_selectFilesPath = "AssetImporter/SelectFilesPath";
const char* g_selectDestinationFilesPath = "AssetImporter/SelectDestinationFilesPath";
const char* g_errorMessageBoxTitle = "File failed to process.";
const char* g_crateError = "Crate files cannot be imported.";
static const char* s_crateFileExtension = "crate";
};
AssetImporterManager::AssetImporterManager(QWidget* parent)
: QObject(parent)
{
}
AssetImporterManager::~AssetImporterManager()
{
}
void AssetImporterManager::Exec()
{
// tell the AssetImporterDragAndDropHandler that the Asset Importer now is running
Q_EMIT StartAssetImporter();
bool success = OnBrowseFiles();
// prevent users from selecting crate files from the File Explorer and open the Asset Importer.
if (!success)
{
reject();
}
else
{
OnOpenSelectDestinationDialog();
}
}
void AssetImporterManager::Exec(const QStringList& dragAndDropFileList)
{
// note: dragging and dropping an empty folder can also trigger this condition
if (!dragAndDropFileList.isEmpty())
{
OnDragAndDropFiles(&dragAndDropFileList);
// only open the Asset Importer when the folder contains correct type files
if (!m_pathMap.isEmpty())
{
// tell the AssetImporterDragAndDropHandler that the Asset Importer now is running
Q_EMIT StartAssetImporter();
OnOpenSelectDestinationDialog();
}
else
{
reject();
}
}
}
// used to cancel actions and close the dialog
void AssetImporterManager::reject()
{
m_pathMap.clear();
m_destinationRootDirectory = "";
Q_EMIT StopAssetImporter();
}
void AssetImporterManager::OnDragAndDropFiles(const QStringList* fileList)
{
for (int i = 0; i < fileList->size(); ++i)
{
// if the list contains a crate file,
// the whole process should stop
if (!GetAndCheckAllFilesInFolder(fileList->at(i)))
{
QMessageBox::warning(AzToolsFramework::GetActiveWindow(), AssetImporterManagerPrivate::g_errorMessageBoxTitle, AssetImporterManagerPrivate::g_crateError);
reject();
return;
}
}
}
bool AssetImporterManager::OnBrowseFiles()
{
QFileDialog fileDialog;
fileDialog.setFileMode(QFileDialog::ExistingFiles);
fileDialog.setWindowModality(Qt::WindowModality::ApplicationModal);
fileDialog.setViewMode(QFileDialog::Detail);
fileDialog.setWindowTitle(tr("Select files to import"));
fileDialog.setLabelText(QFileDialog::Accept, "Select");
QSettings settings;
QString currentAbsolutePath = settings.value(AssetImporterManagerPrivate::g_selectFilesPath).toString();
QDir gameRoot(Path::GetEditingGameDataFolder().c_str());
QString gameRootAbsPath = gameRoot.absolutePath();
// Case 1: if currentAbsolutePath is empty at this point, that means this is the first time
// users using the Asset Importer, set the default directory to be users' PC's desktop.
// 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 under the game root folder, then in general,
// users have modified the folder directory in the registry. It should not be happening.
if (currentAbsolutePath.isEmpty() || !QFile(currentAbsolutePath).exists() || currentAbsolutePath.startsWith(gameRootAbsPath, Qt::CaseInsensitive))
{
currentAbsolutePath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
}
fileDialog.setDirectory(currentAbsolutePath);
if (!fileDialog.exec())
{
return false;
}
bool encounteredCrate = false;
QStringList invalidFiles;
for (QString path : fileDialog.selectedFiles())
{
QString fileName = GetFileName(path);
QFileInfo info(path);
QString extension = info.completeSuffix(); // extension without '.'
if (QString(AssetImporterManagerPrivate::s_crateFileExtension).compare(extension, Qt::CaseInsensitive) != 0)
{
// prevent users from importing files under the game root directory
if (path.startsWith(gameRootAbsPath, Qt::CaseInsensitive))
{
invalidFiles << fileName;
}
else
{
// store paths into the map.
m_pathMap[path] = fileName;
}
}
else
{
encounteredCrate = true;
}
}
if (invalidFiles.size() > 0)
{
QString fileWarning = QString("Files cannot be imported into their own project. The following files will not be moved or copied:\n");
fileWarning.append(invalidFiles.join(", "));
fileWarning.append('.');
QMessageBox::warning(AzToolsFramework::GetActiveWindow(), AssetImporterManagerPrivate::g_errorMessageBoxTitle, fileWarning);
}
if (encounteredCrate)
{
QMessageBox::warning(AzToolsFramework::GetActiveWindow(), AssetImporterManagerPrivate::g_errorMessageBoxTitle, AssetImporterManagerPrivate::g_crateError);
}
currentAbsolutePath = fileDialog.directory().absolutePath();
settings.setValue(AssetImporterManagerPrivate::g_selectFilesPath, currentAbsolutePath);
// prevent users from selecting crate files from the File Explorer and open the Asset Importer.
return (m_pathMap.size() > 0);
}
void AssetImporterManager::OnBrowseDestinationFilePath(QLineEdit* destinationLineEdit)
{
QFileDialog fileDialog;
fileDialog.setOption(QFileDialog::ShowDirsOnly, true);
fileDialog.setViewMode(QFileDialog::List);
fileDialog.setWindowModality(Qt::WindowModality::ApplicationModal);
fileDialog.setWindowTitle(tr("Select import destination"));
QSettings settings;
QString currentDestination = settings.value(AssetImporterManagerPrivate::g_selectDestinationFilesPath).toString();
QDir gameRoot(Path::GetEditingGameDataFolder().c_str());
QString gameRootAbsPath = gameRoot.absolutePath();
// 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 (currentDestination.isEmpty() || !QDir(currentDestination).exists() || !currentDestination.startsWith(gameRootAbsPath, Qt::CaseInsensitive))
{
currentDestination = gameRootAbsPath;
}
fileDialog.setDirectory(currentDestination);
// The default file path is the game project root folder.
// After that, the default file path will be the previous opened folder path.
connect(&fileDialog, &QFileDialog::directoryEntered, this, [&fileDialog, &gameRoot, &gameRootAbsPath](const QString& path)
{
// get current relative path
QString relativePath = gameRoot.relativeFilePath(path);
// Guard against navigating outside of the project folder. Lambda used as the dialog had to be captured.
// checking the directory and prevent users from changing the directory outside of the game root
if (!path.startsWith(gameRootAbsPath, Qt::CaseInsensitive) || (relativePath.length() > 2 && relativePath[0] == '.' && relativePath[1] == '.'))
{
fileDialog.setDirectory(gameRoot);
}
});
if (!fileDialog.exec())
{
return;
}
// users can only select one folder at a time, so the index is always 0.
// This fixes the issue that QFileDialog does not select the highlighted folder
QString destinationDirectory = fileDialog.selectedFiles().at(0);
OnSetDestinationDirectory(destinationDirectory);
destinationLineEdit->setText(destinationDirectory);
}
// Copy + Paste
void AssetImporterManager::OnCopyFiles()
{
m_importMethod = ImportFilesMethod::CopyFiles;
ProcessCopyFiles();
}
// Cut + Paste
void AssetImporterManager::OnMoveFiles()
{
m_importMethod = ImportFilesMethod::MoveFiles;
ProcessMoveFiles();
}
bool AssetImporterManager::OnOverwriteFiles(QString relativePath, QString oldAbsolutePath)
{
// this is the absolute path in the destination folder
QString destinationAbsolutePath = GenerateAbsolutePath(relativePath);
return Overwrite(relativePath, oldAbsolutePath, destinationAbsolutePath);
}
bool AssetImporterManager::OnKeepBothFiles(QString relativePath, QString oldAbsolutePath)
{
// this is the absolute path in the destination folder
QString destinationAbsolutePath = GenerateAbsolutePath(relativePath);
QString subPath = QFileInfo(destinationAbsolutePath).absoluteDir().absolutePath();
QFileInfo info(destinationAbsolutePath);
QString extension = info.completeSuffix(); // extension without '.'
QString fileName = info.baseName(); //file name without extension
int number = 1;
int index = destinationAbsolutePath.indexOf(extension);
QString newFileName = CreateFileNameWithNumber(number, fileName, index, extension);
QString newDestinationAbsolutePath = subPath + '/' + newFileName;
while (QFile(newDestinationAbsolutePath).exists())
{
number++;
newFileName = CreateFileNameWithNumber(number, fileName, index, extension);
newDestinationAbsolutePath = subPath + '/' + newFileName;
}
if (m_importMethod == ImportFilesMethod::CopyFiles)
{
return Copy(relativePath, oldAbsolutePath, newDestinationAbsolutePath);
}
else if (m_importMethod == ImportFilesMethod::MoveFiles)
{
return Move(relativePath, oldAbsolutePath, newDestinationAbsolutePath);
}
return false;
}
void AssetImporterManager::OnOpenLogDialog()
{
AzFramework::AssetSystemRequestBus::Broadcast(&AzFramework::AssetSystem::AssetSystemRequests::ShowAssetProcessor);
reject();
}
void AssetImporterManager::OnSetDestinationDirectory(QString destinationDirectory)
{
QSettings settings;
QString currentDestination = settings.value(AssetImporterManagerPrivate::g_selectDestinationFilesPath).toString();
m_destinationRootDirectory = (!destinationDirectory.isEmpty()) ? destinationDirectory : currentDestination;
settings.setValue(AssetImporterManagerPrivate::g_selectDestinationFilesPath, destinationDirectory);
}
void AssetImporterManager::OnOpenSelectDestinationDialog()
{
QWidget* mainWindow = nullptr;
AzToolsFramework::EditorRequestBus::BroadcastResult(mainWindow, &AzToolsFramework::EditorRequests::GetMainWindow);
QString numberOfFilesMessage = m_pathMap.size() == 1 ? QString(tr("Importing 1 asset")) : QString(tr("Importing %1 assets").arg(m_pathMap.size()));
SelectDestinationDialog selectDestinationDialog(numberOfFilesMessage, mainWindow);
// Browse Destination File Path
connect(&selectDestinationDialog, &SelectDestinationDialog::BrowseDestinationPath, this, &AssetImporterManager::OnBrowseDestinationFilePath);
connect(&selectDestinationDialog, &SelectDestinationDialog::DoCopyFiles, this, &AssetImporterManager::OnCopyFiles);
connect(&selectDestinationDialog, &SelectDestinationDialog::DoMoveFiles, this, &AssetImporterManager::OnMoveFiles);
connect(&selectDestinationDialog, &SelectDestinationDialog::Cancel, this, &AssetImporterManager::reject);
connect(&selectDestinationDialog, &SelectDestinationDialog::SetDestinationDirectory, this, &AssetImporterManager::OnSetDestinationDirectory);
selectDestinationDialog.exec();
}
ProcessFilesMethod AssetImporterManager::OnOpenFilesAlreadyExistDialog(QString message, int numberOfFiles)
{
ProcessFilesMethod processMethod = ProcessFilesMethod::Default;
// make sure the dialog is opened in front of the Editor main window
QWidget* mainWindow = nullptr;
AzToolsFramework::EditorRequestBus::BroadcastResult(mainWindow, &AzToolsFramework::EditorRequests::GetMainWindow);
FilesAlreadyExistDialog filesAlreadyExistDialog(message, numberOfFiles, mainWindow);
bool applyToAll = false;
connect(&filesAlreadyExistDialog, &FilesAlreadyExistDialog::ApplyActionToAllFiles, this, [&applyToAll](bool result)
{
applyToAll = result;
});
connect(&filesAlreadyExistDialog, &FilesAlreadyExistDialog::OverWriteFiles, this, [this, &processMethod, &applyToAll]()
{
processMethod = UpdateProcessFileMethod(ProcessFilesMethod::OverwriteFile, applyToAll);
});
connect(&filesAlreadyExistDialog, &FilesAlreadyExistDialog::KeepBothFiles, this, [this, &processMethod, &applyToAll]()
{
processMethod = UpdateProcessFileMethod(ProcessFilesMethod::KeepBothFile, applyToAll);
});
connect(&filesAlreadyExistDialog, &FilesAlreadyExistDialog::SkipCurrentProcess, this, [this, &processMethod, &applyToAll]()
{
processMethod = UpdateProcessFileMethod(ProcessFilesMethod::SkipProcessingFile, applyToAll);
});
connect(&filesAlreadyExistDialog, &FilesAlreadyExistDialog::CancelAllProcesses, this, [&processMethod]()
{
processMethod = ProcessFilesMethod::Cancel;
});
if (!applyToAll && processMethod != ProcessFilesMethod::Cancel)
{
filesAlreadyExistDialog.exec();
}
return processMethod;
}
ProcessFilesMethod AssetImporterManager::UpdateProcessFileMethod(ProcessFilesMethod processMethod, bool applyToAll)
{
if (applyToAll)
{
switch (processMethod)
{
case ProcessFilesMethod::OverwriteFile:
processMethod = ProcessFilesMethod::OverwriteAllFiles;
break;
case ProcessFilesMethod::KeepBothFile:
processMethod = ProcessFilesMethod::KeepBothAllFiles;
break;
case ProcessFilesMethod::SkipProcessingFile:
processMethod = ProcessFilesMethod::SkipProcessingAllFiles;
}
}
return processMethod;
}
bool AssetImporterManager::ProcessFileMethod(ProcessFilesMethod processMethod, QString relativePath, QString oldAbsolutePath)
{
switch (processMethod)
{
case ProcessFilesMethod::OverwriteFile:
case ProcessFilesMethod::OverwriteAllFiles:
return OnOverwriteFiles(relativePath, oldAbsolutePath);
case ProcessFilesMethod::KeepBothFile:
case ProcessFilesMethod::KeepBothAllFiles:
return OnKeepBothFiles(relativePath, oldAbsolutePath);
case ProcessFilesMethod::SkipProcessingAllFiles:
return false;
}
return false;
}
void AssetImporterManager::OnOpenProcessingAssetsDialog(int numberOfProcessedFiles)
{
// make sure the dialog is opened in front of the Editor main window
QWidget* mainWindow = nullptr;
AzToolsFramework::EditorRequestBus::BroadcastResult(mainWindow, &AzToolsFramework::EditorRequests::GetMainWindow);
ProcessingAssetsDialog processingAssetsDialog(numberOfProcessedFiles, mainWindow);
connect(&processingAssetsDialog, &ProcessingAssetsDialog::OpenLogDialog, this, &AssetImporterManager::OnOpenLogDialog);
connect(&processingAssetsDialog, &ProcessingAssetsDialog::CloseProcessingAssetsDialog, this, &AssetImporterManager::reject);
processingAssetsDialog.exec();
}
void AssetImporterManager::ProcessCopyFiles()
{
int numberOfFiles = m_pathMap.size();
int numberOfProcessedFiles = 0;
ProcessFilesMethod processMethod = ProcessFilesMethod::Default;
for (int i = 0; i < m_pathMap.size(); ++i)
{
QString relativePath = m_pathMap.values().at(i);
QString oldAbsolutePath = m_pathMap.keys().at(i);
// this is the absolute path in the destination folder
QString destinationAbsolutePath = GenerateAbsolutePath(relativePath);
// check if the file exists in the destination folder
if (!QFile::exists(destinationAbsolutePath))
{
if (Copy(relativePath, oldAbsolutePath, destinationAbsolutePath))
{
numberOfProcessedFiles++;
}
}
else
{
if (processMethod == ProcessFilesMethod::Default ||
processMethod == ProcessFilesMethod::OverwriteFile ||
processMethod == ProcessFilesMethod::KeepBothFile ||
processMethod == ProcessFilesMethod::SkipProcessingFile)
{
QString fileName = GetFileName(oldAbsolutePath);
QString message = QString("The destination already has a file named \"%1\". What would you like to do?").arg(fileName);
processMethod = OnOpenFilesAlreadyExistDialog(message, numberOfFiles);
}
if (ProcessFileMethod(processMethod, relativePath, oldAbsolutePath))
{
numberOfProcessedFiles++;
}
}
numberOfFiles--;
}
if (numberOfProcessedFiles > 0)
{
OnOpenProcessingAssetsDialog(numberOfProcessedFiles);
}
else
{
reject();
}
}
void AssetImporterManager::ProcessMoveFiles()
{
int numberOfFiles = m_pathMap.size();
int numberOfProcessedFiles = 0;
ProcessFilesMethod processMethod = ProcessFilesMethod::Default;
for (int i = 0; i < m_pathMap.size(); ++i)
{
QString relativePath = m_pathMap.values().at(i);
QString oldAbsolutePath = m_pathMap.keys().at(i);
// this is the absolute path in the destination folder
QString destinationAbsolutePath = GenerateAbsolutePath(relativePath);
// check if the file exists in the destination folder
if (!QFile::exists(destinationAbsolutePath))
{
if (Move(relativePath, oldAbsolutePath, destinationAbsolutePath))
{
numberOfProcessedFiles++;
}
}
else
{
if (processMethod == ProcessFilesMethod::Default ||
processMethod == ProcessFilesMethod::OverwriteFile ||
processMethod == ProcessFilesMethod::KeepBothFile ||
processMethod == ProcessFilesMethod::SkipProcessingFile)
{
QString fileName = GetFileName(oldAbsolutePath);
QString message = QString("The destination already has a file named \"%1\". What would you like to do?").arg(fileName);
processMethod = OnOpenFilesAlreadyExistDialog(message, numberOfFiles);
}
if (ProcessFileMethod(processMethod, relativePath, oldAbsolutePath))
{
numberOfProcessedFiles++;
}
}
numberOfFiles--;
}
if (numberOfProcessedFiles > 0)
{
OnOpenProcessingAssetsDialog(numberOfProcessedFiles);
}
else
{
reject();
}
}
bool AssetImporterManager::Copy(QString relativePath, QString oldAbsolutePath, QString destinationAbsolutePath)
{
QString fileName = GetFileName(destinationAbsolutePath);
QString subPath = QFileInfo(destinationAbsolutePath).absoluteDir().absolutePath();
QDir dir;
bool directoryExistedAlready = QDir(subPath).exists();
if (!directoryExistedAlready)
{
dir.mkpath(subPath);
}
QString newDestinationAbsolutePath = subPath;
newDestinationAbsolutePath = newDestinationAbsolutePath.append('/' + fileName);
// Copy the file from the old path to the new path
if (!QFile::copy(oldAbsolutePath, newDestinationAbsolutePath))
{
QString reason = tr("an unknown issue occurred.");
if (!directoryExistedAlready)
{
dir.rmdir(subPath);
}
// if the original files got deleted at this condition
if (!QFile(oldAbsolutePath).exists())
{
reason = tr("%1 no longer exists.").arg(fileName);
}
// if users manually copy the file into the destination folder at this condition
if (QFile(newDestinationAbsolutePath).exists())
{
reason = tr("%1 already exists in the target directory.").arg(fileName);
}
QMessageBox::critical(AzToolsFramework::GetActiveWindow(), AssetImporterManagerPrivate::g_errorMessageBoxTitle, QObject::tr("We're sorry, but the file failed to process because %1").arg(reason));
return false;
}
// set the destination file to be writable by user himself/herself if it's read-only
QFile destinationFile(newDestinationAbsolutePath);
SetDestinationFileWritable(destinationFile);
return true;
}
bool AssetImporterManager::Move(QString relativePath, QString oldAbsolutePath, QString destinationAbsolutePath)
{
QString fileName = GetFileName(destinationAbsolutePath);
QString subPath = QFileInfo(destinationAbsolutePath).absoluteDir().absolutePath();
QString newDestinationAbsolutePath = subPath;
QDir dir;
bool directoryExistedAlready = QDir(subPath).exists();
if (!directoryExistedAlready)
{
dir.mkpath(subPath);
}
if (QFile::rename(oldAbsolutePath, newDestinationAbsolutePath.append('/' + fileName)))
{
QString oldFileName = GetFileName(oldAbsolutePath);
// Only remove the old directory if the relative path is the file name itself.
// That also means users are dragging and dropping files, but not a folder containing those files.
if (oldFileName.compare(relativePath) != 0)
{
RemoveOldPath(oldAbsolutePath, relativePath);
}
// set the destination file to be writable by user himself/herself if it's read-only
QFile destinationFile(newDestinationAbsolutePath);
SetDestinationFileWritable(destinationFile);
return true;
}
else
{
QString reason = tr("an unknown issue occurred.");
QString oldFileName = GetFileName(oldAbsolutePath);
if (!directoryExistedAlready)
{
dir.rmdir(subPath);
}
// if the original files got deleted at this condition
if (!QFile(oldAbsolutePath).exists())
{
reason = tr("%1 no longer exists.").arg(oldFileName);
}
// if users manually copy the file into the destination folder at this condition
if (QFile(newDestinationAbsolutePath).exists())
{
reason = tr("%1 already exists in the target directory.").arg(oldFileName);
}
QMessageBox::critical(AzToolsFramework::GetActiveWindow(), AssetImporterManagerPrivate::g_errorMessageBoxTitle, QObject::tr("We're sorry, but the file failed to process because %1").arg(reason));
return false;
}
}
bool AssetImporterManager::Overwrite(QString relativePath, QString oldAbsolutePath, QString destinationAbsolutePath)
{
QFile newFile(destinationAbsolutePath);
QFile oldFile(oldAbsolutePath);
// double check if paths are valid
if ((!oldFile.open(QIODevice::ReadOnly)))
{
QString fileName = GetFileName(oldAbsolutePath);
QString reason = tr("%1 no longer exists.").arg(fileName);
QMessageBox::critical(AzToolsFramework::GetActiveWindow(), AssetImporterManagerPrivate::g_errorMessageBoxTitle, QObject::tr("We're sorry, but the file failed to process because %1").arg(reason));
return false;
}
if (!newFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
QString reason = tr("We're sorry, but the file failed to process.");
QString fileName = GetFileName(destinationAbsolutePath);
if (!newFile.exists())
{
reason = tr("%1 from the destination directory is removed.").arg(fileName);
}
else
{
reason = tr("%1 from the destination directory cannot be overwritten.").arg(fileName);
}
QMessageBox::critical(AzToolsFramework::GetActiveWindow(), AssetImporterManagerPrivate::g_errorMessageBoxTitle, QObject::tr("We're sorry, but the file failed to process because %1").arg(reason));
return false;
}
QDataStream dataStream(&oldFile);
QDataStream out(&newFile);
int bufferSize = 1024 * 1024;
char* buffer = new char[bufferSize];
while (!dataStream.atEnd())
{
int bytesRead = dataStream.readRawData(buffer, bufferSize);
out.writeRawData(buffer, bytesRead);
}
delete[] buffer;
oldFile.close();
newFile.close();
// if it's the move file method, got to remove the original files
if (m_importMethod == ImportFilesMethod::MoveFiles)
{
QString fileName = GetFileName(oldAbsolutePath);
QFile file(oldAbsolutePath);
QDir absoluteDir = QFileInfo(oldAbsolutePath).absoluteDir();
if (file.exists())
{
// if the original file is read-only,
// then it got to be writable in order to be deleted successfully
SetDestinationFileWritable(file);
absoluteDir.remove(fileName);
}
// Only remove the old directory if the relative path is the file name itself.
// That also means users are dragging and dropping files, but not a folder containing those files.
if (fileName.compare(relativePath) != 0)
{
RemoveOldPath(oldAbsolutePath, relativePath);
}
}
return true;
}
bool AssetImporterManager::GetAndCheckAllFilesInFolder(QString path)
{
QString formattedPath = path;
// Paths ending with '/' return from QFileInfo().fileName() with a value of ""
// Strip a trailing slash so we can correctly get rootFolderName on all platforms
if (formattedPath.endsWith("/"))
{
formattedPath.truncate(formattedPath.lastIndexOf(QChar('/')));
}
QString rootFolderName = GetFileName(formattedPath);
QDirIterator it(formattedPath, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::Subdirectories);
QFileInfo info(formattedPath);
if (!info.isDir() && !it.hasNext() && info.exists())
{
m_pathMap[formattedPath] = rootFolderName;
return true;
}
// Get the index of the last sub folder name in the path
QStringList directoryNameList = formattedPath.split('/');
int lastFolderIndex = directoryNameList.size() - 1;
QString pathToBeRelativeTo = directoryNameList.mid(0, lastFolderIndex).join('/');
while (it.hasNext())
{
QString absolutePath = it.next();
QFileInfo absoluteInfo(absolutePath);
QString extension = absoluteInfo.completeSuffix();
if (QString(AssetImporterManagerPrivate::s_crateFileExtension).compare(extension, Qt::CaseInsensitive) == 0)
{
return false;
}
Q_ASSERT(absolutePath.startsWith(pathToBeRelativeTo));
QString relativePath = absolutePath.mid(pathToBeRelativeTo.size() + 1);
m_pathMap[absolutePath] = relativePath;
}
return true;
}
void AssetImporterManager::RemoveOldPath(QString oldAbsolutePath, QString oldRelativePath)
{
QDir absoluteDir = QFileInfo(oldAbsolutePath).absoluteDir();
QStringList directoryList = oldRelativePath.split('/');
// remove each folder from the leave to the root, based on the relative path
for (int i = 0; i < directoryList.size(); ++i)
{
QString currentDir = absoluteDir.path();
absoluteDir.rmpath(currentDir);
absoluteDir.cdUp();
}
}
void AssetImporterManager::SetDestinationFileWritable(QFile& destinationFile)
{
if (destinationFile.open(QIODevice::ReadOnly))
{
destinationFile.setPermissions(QFile::WriteOwner | destinationFile.permissions());
}
destinationFile.close();
}
QString AssetImporterManager::CreateFileNameWithNumber(int number, QString fileName, int index, QString extension)
{
QString newFileName;
newFileName = fileName.isEmpty() ? fileName : fileName.left(index);
newFileName += "(" + QString::number(number) + ")";
if (extension.size() > 0)
{
newFileName += "." + extension;
}
return newFileName;
}
QString AssetImporterManager::GenerateAbsolutePath(QString relativePath)
{
return QDir(m_destinationRootDirectory).absoluteFilePath(relativePath);
}
QString AssetImporterManager::GetFileName(QString path)
{
return QFileInfo(path).fileName();
}
#include <AssetImporter/AssetImporterManager/moc_AssetImporterManager.cpp>
@@ -0,0 +1,94 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QMainWindow>
#include <AssetImporter/UI/SelectDestinationDialog.h>
#endif
class QStringList;
class QFile;
enum class ImportFilesMethod
{
CopyFiles,
MoveFiles
};
enum class ProcessFilesMethod
{
OverwriteFile,
KeepBothFile,
SkipProcessingFile,
OverwriteAllFiles,
KeepBothAllFiles,
SkipProcessingAllFiles,
Cancel,
Default
};
class AssetImporterManager
: public QObject
{
Q_OBJECT
public:
explicit AssetImporterManager(QWidget* parent = nullptr);
~AssetImporterManager();
// Modal, but blocking.
void Exec(); // for browsing files
void Exec(const QStringList& dragAndDropFileList); // for drag and drop
Q_SIGNALS:
void StartAssetImporter();
void StopAssetImporter();
private Q_SLOTS:
void reject();
void OnDragAndDropFiles(const QStringList* fileList);
bool OnBrowseFiles();
void OnBrowseDestinationFilePath(QLineEdit* destinationLineEdit);
void OnCopyFiles();
void OnMoveFiles();
bool OnOverwriteFiles(QString relativePath, QString oldAbsolutePath);
bool OnKeepBothFiles(QString relativePath, QString oldAbsolutePath);
void OnOpenLogDialog();
void OnSetDestinationDirectory(QString destinationDirectory);
private:
void OnOpenSelectDestinationDialog();
ProcessFilesMethod OnOpenFilesAlreadyExistDialog(QString message, int numberOfFiles);
ProcessFilesMethod UpdateProcessFileMethod(ProcessFilesMethod processMethod, bool applyToAll);
bool ProcessFileMethod(ProcessFilesMethod processMethod, QString relativePath, QString oldAbsolutePath);
void OnOpenProcessingAssetsDialog(int numberOfProcessedFiles);
void ProcessCopyFiles();
void ProcessMoveFiles();
bool Copy(QString relativePath, QString oldAbsolutePath, QString destinationAbsolutePath);
bool Move(QString relativePath, QString oldAbsolutePath, QString destinationAbsolutePath);
bool Overwrite(QString relativePath, QString oldAbsolutePath, QString destinationAbsolutePath);
bool GetAndCheckAllFilesInFolder(QString path);
void RemoveOldPath(QString oldAbsolutePath, QString oldRelativePath);
void SetDestinationFileWritable(QFile& destinationFile);
QString CreateFileNameWithNumber(int number, QString fileName, int index, QString extension);
QString GenerateAbsolutePath(QString relativePath);
QString GetFileName(QString path);
ImportFilesMethod m_importMethod;
// Key = absolute path, Value = relative path
QMap<QString, QString> m_pathMap;
QString m_destinationRootDirectory;
};
@@ -0,0 +1,100 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "FilesAlreadyExistDialog.h"
// Qt
#include <QPushButton>
#include <QStyle>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <AssetImporter/UI/ui_FilesAlreadyExistDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
FilesAlreadyExistDialog::FilesAlreadyExistDialog(QString message ,int numberOfFiles, QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::FilesAlreadyExistDialog)
{
m_ui->setupUi(this);
UpdateMessage(message);
InitializeButtons();
UpdateCheckBoxState(numberOfFiles);
}
FilesAlreadyExistDialog::~FilesAlreadyExistDialog()
{
}
void FilesAlreadyExistDialog::InitializeButtons()
{
m_ui->buttonBox->setContentsMargins(0, 0, 16, 16);
QPushButton* overwriteButton = m_ui->buttonBox->addButton(tr("Overwrite"), QDialogButtonBox::AcceptRole);
QPushButton* keepBothButton = m_ui->buttonBox->addButton(tr("Keep Both"), QDialogButtonBox::AcceptRole);
QPushButton* skipButton = m_ui->buttonBox->addButton(tr("Skip"), QDialogButtonBox::AcceptRole);
overwriteButton->setProperty("class", "Primary");
overwriteButton->setDefault(true);
keepBothButton->setProperty("class", "AssetImporterLargerButton");
keepBothButton->style()->unpolish(keepBothButton);
keepBothButton->style()->polish(keepBothButton);
keepBothButton->update();
skipButton->setProperty("class", "AssetImporterButton");
skipButton->style()->unpolish(skipButton);
skipButton->style()->polish(skipButton);
skipButton->update();
connect(overwriteButton, &QPushButton::clicked, this, &FilesAlreadyExistDialog::DoOverwrite);
connect(keepBothButton, &QPushButton::clicked, this, &FilesAlreadyExistDialog::DoKeepBoth);
connect(skipButton, &QPushButton::clicked, this, &FilesAlreadyExistDialog::DoSkipCurrentProcess);
}
void FilesAlreadyExistDialog::UpdateMessage(QString message)
{
m_ui->message->setText(message);
}
void FilesAlreadyExistDialog::DoSkipCurrentProcess()
{
QDialog::accept();
Q_EMIT SkipCurrentProcess();
}
void FilesAlreadyExistDialog::DoOverwrite()
{
QDialog::accept();
Q_EMIT OverWriteFiles();
}
void FilesAlreadyExistDialog::DoKeepBoth()
{
QDialog::accept();
Q_EMIT KeepBothFiles();
}
void FilesAlreadyExistDialog::DoApplyActionToAllFiles()
{
Q_EMIT ApplyActionToAllFiles(m_ui->applyToAllCheckBox->isChecked());
}
void FilesAlreadyExistDialog::UpdateCheckBoxState(int numberOfFiles)
{
m_ui->applyToAllCheckBox->setVisible((numberOfFiles > 1));
connect(m_ui->applyToAllCheckBox, &QCheckBox::stateChanged, this, &FilesAlreadyExistDialog::DoApplyActionToAllFiles);
}
void FilesAlreadyExistDialog::closeEvent([[maybe_unused]] QCloseEvent* ev)
{
QDialog::reject();
Q_EMIT CancelAllProcesses();
}
#include <AssetImporter/UI/moc_FilesAlreadyExistDialog.cpp>
@@ -0,0 +1,47 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QDialog>
#endif
class QTreeView;
namespace Ui {
class FilesAlreadyExistDialog;
}
class FilesAlreadyExistDialog
: public QDialog
{
Q_OBJECT
public:
FilesAlreadyExistDialog(QString message, int numberOfFiles, QWidget* parent = nullptr);
~FilesAlreadyExistDialog();
Q_SIGNALS:
void OverWriteFiles();
void KeepBothFiles();
void SkipCurrentProcess();
void CancelAllProcesses();
void ApplyActionToAllFiles(bool result);
public Q_SLOTS:
void DoSkipCurrentProcess();
void DoOverwrite();
void DoKeepBoth();
void DoApplyActionToAllFiles();
private:
void InitializeButtons();
void UpdateMessage(QString message);
void UpdateCheckBoxState(int numberOfFiles);
void closeEvent(QCloseEvent* ev) override;
QScopedPointer<Ui::FilesAlreadyExistDialog> m_ui;
};
@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FilesAlreadyExistDialog</class>
<widget class="QWidget" name="FilesAlreadyExistDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>587</width>
<height>159</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>571</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="windowTitle">
<string>Replace or Skip Files</string>
</property>
<property name="class" stdset="0">
<string>AssetImporterDialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>16</number>
</property>
<property name="topMargin">
<number>16</number>
</property>
<property name="rightMargin">
<number>16</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="message">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>The destination already has a file named &quot;&quot;. What would you like to do?</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="class" stdset="0">
<string>AssetImporterLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line">
<property name="minimumSize">
<size>
<width>584</width>
<height>2</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>16</number>
</property>
<item>
<widget class="QCheckBox" name="applyToAllCheckBox">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Apply to all</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::NoButton</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,79 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "ProcessingAssetsDialog.h"
// Qt
#include <QPushButton>
#include <QStyle>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <AssetImporter/UI/ui_ProcessingAssetsDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
ProcessingAssetsDialog::ProcessingAssetsDialog(int numberOfProcessedFiles, QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::ProcessingAssetsDialog)
{
m_ui->setupUi(this);
UpdateTextsAndTitle(numberOfProcessedFiles);
InitializeButtons();
}
ProcessingAssetsDialog::~ProcessingAssetsDialog()
{
}
void ProcessingAssetsDialog::InitializeButtons()
{
QPushButton* viewStatusButton = m_ui->buttonBox->addButton(tr("View status"), QDialogButtonBox::AcceptRole);
QPushButton* closeButton = m_ui->buttonBox->addButton(tr("Close"), QDialogButtonBox::RejectRole);
viewStatusButton->setDefault(true);
viewStatusButton->setProperty("class", "AssetImporterLargerButton");
viewStatusButton->style()->unpolish(viewStatusButton);
viewStatusButton->style()->polish(viewStatusButton);
viewStatusButton->update();
closeButton->setProperty("class", "AssetImporterButton");
closeButton->style()->unpolish(closeButton);
closeButton->style()->polish(closeButton);
closeButton->update();
connect(viewStatusButton, &QPushButton::clicked, this, &ProcessingAssetsDialog::accept);
connect(closeButton, &QPushButton::clicked, this, &ProcessingAssetsDialog::reject);
}
void ProcessingAssetsDialog::accept()
{
Q_EMIT OpenLogDialog();
QDialog::accept();
}
void ProcessingAssetsDialog::reject()
{
Q_EMIT CloseProcessingAssetsDialog();
QDialog::reject();
}
void ProcessingAssetsDialog::UpdateTextsAndTitle(int numberOfProcessedFiles)
{
if (numberOfProcessedFiles > 1)
{
setWindowTitle("Processing assets");
m_ui->label->setText("The Asset Processor will process your assets and when they are finished they will appear in the Asset Browser. You can view the status of your assets in the Asset Processor.");
}
else
{
setWindowTitle("Processing asset");
m_ui->label->setText("The Asset Processor will process your asset and when it is finished it will appear in the Asset Browser. You can view the status of your asset in the Asset Processor.");
}
}
#include <AssetImporter/UI/moc_ProcessingAssetsDialog.cpp>
@@ -0,0 +1,39 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QDialog>
#endif
namespace Ui {
class ProcessingAssetsDialog;
}
class ProcessingAssetsDialog
: public QDialog
{
Q_OBJECT
public:
ProcessingAssetsDialog(int numberOfProcessedFiles, QWidget* parent = nullptr);
~ProcessingAssetsDialog();
void InitializeButtons();
Q_SIGNALS:
void CloseProcessingAssetsDialog();
void OpenLogDialog();
public Q_SLOTS:
void accept();
void reject();
private:
void UpdateTextsAndTitle(int numberOfProcessedFiles);
QScopedPointer<Ui::ProcessingAssetsDialog> m_ui;
};
@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProcessingAssetsDialog</class>
<widget class="QWidget" name="ProcessingAssetsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>540</width>
<height>187</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>440</width>
<height>187</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="windowTitle">
<string>Processing assets</string>
</property>
<property name="class" stdset="0">
<string>AssetImporterDialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>16</number>
</property>
<property name="topMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>16</number>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>408</width>
<height>76</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
<kerning>false</kerning>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="class" stdset="0">
<string>AssetImporterLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>405</width>
<height>24</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>408</width>
<height>28</height>
</size>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::NoButton</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,251 @@
/*
* 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>
@@ -0,0 +1,58 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzQtComponents/Components/StyledLineEdit.h>
#include <QLabel>
#include <QDialog>
#include <QDialogButtonBox>
#endif
class StyledLineEdit;
class QValidator;
class DestinationDialogValidator;
namespace Ui {
class SelectDestinationDialog;
}
class SelectDestinationDialog
: public QDialog
{
Q_OBJECT
public:
SelectDestinationDialog(QString message, QWidget* parent = nullptr);
~SelectDestinationDialog();
Q_SIGNALS:
void GoBack();
void DoCopyFiles();
void DoMoveFiles();
void BrowseDestinationPath(QLineEdit* destinationLineEdit);
void Cancel();
void UpdateImportButtonState(bool enabled);
void SetDestinationDirectory(QString destinationDirectory);
public Q_SLOTS:
void accept();
void reject();
void ShowMessage();
void OnBrowseDestinationFilePath();
void ValidatePath();
private:
void UpdateMessage(QString message);
void InitializeButtons();
void SetPreviousDestinationDirectory();
QString DestinationDirectory() const;
QScopedPointer<Ui::SelectDestinationDialog> m_ui;
DestinationDialogValidator* m_validator;
};
@@ -0,0 +1,500 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SelectDestinationDialog</class>
<widget class="QWidget" name="SelectDestinationDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>587</width>
<height>409</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>587</width>
<height>409</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="windowTitle">
<string>Import Asset(s)</string>
</property>
<property name="class" stdset="0">
<string>AssetImporterDialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="mainVerticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>16</number>
</property>
<property name="topMargin">
<number>16</number>
</property>
<property name="rightMargin">
<number>16</number>
</property>
<item>
<widget class="QLabel" name="NumberOfFilesMessage">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>329</width>
<height>19</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Importing 0 asset(s).</string>
</property>
<property name="class" stdset="0">
<string>AssetImporterLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="DestinationFolderLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Destination Folder</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="class" stdset="0">
<string>AssetImporterLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>8</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::StyledLineEdit" name="DestinationLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>28</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="class" stdset="0">
<string>AssetImporterLineEdit</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>8</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="BrowseButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>76</width>
<height>28</height>
</size>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="QuestionMessage">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>19</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>How should we import these file(s)?</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="class" stdset="0">
<string>AssetImporterLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="radioButtonHorizontalLayout">
<property name="spacing">
<number>16</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QRadioButton" name="CopyFileRadioButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Copy files</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="class" stdset="0">
<string>AssetImporterRadioButton</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="MoveFileRadioButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string>Move files</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="class" stdset="0">
<string>AssetImporterRadioButton</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="CopySpacer1">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="RaidoButtonMessage">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>72</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="class" stdset="0">
<string>AssetImporterLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="MoveSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>584</width>
<height>2</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::NoButton</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::StyledLineEdit</class>
<extends>QLineEdit</extends>
<header>AzQtComponents/Components/StyledLineEdit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>