Merge branch 'development' into Atom/guthadam/material_editor_replace_modified_color_with_indicator
Signed-off-by: Guthrie Adams <guthadam@amazon.com> # Conflicts: # Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp # Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h # Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.cpp # Gems/AtomLyIntegration/CommonFeatures/Code/Source/Material/EditorMaterialComponentInspector.h
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
|
||||
@@ -12,95 +12,84 @@
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Math/Uuid.h>
|
||||
#include <future>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
// use bind if you need additional context.
|
||||
// Parameters:
|
||||
// bool - If the archive command was successful or not.
|
||||
typedef AZStd::function<void(bool)> ArchiveResponseCallback;
|
||||
// bool - If the archive command was successful or not.
|
||||
// AZStd::string - The console output from the command.
|
||||
typedef AZStd::function<void(bool, AZStd::string)> ArchiveResponseOutputCallback;
|
||||
|
||||
|
||||
//! ArchiveCommands
|
||||
//! This bus handles messages relating to archive commands
|
||||
//! archive commands are ASYNCHRONOUS
|
||||
//! archive formats officially supported are .zip
|
||||
//! do not block the main thread waiting for a response, it is not okay
|
||||
//! you will not get a message delivered unless you tick the tickbus anyway!
|
||||
class ArchiveCommands
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
|
||||
using Bus = AZ::EBus<ArchiveCommands>;
|
||||
|
||||
// EBus Traits
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
typedef AZStd::recursive_mutex MutexType;
|
||||
static const bool LocklessDispatch = true;
|
||||
virtual ~ArchiveCommands() {}
|
||||
|
||||
//! Start an async task to extract an archive to the target directory
|
||||
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
|
||||
//! Multiple tasks can be associated with the same handle
|
||||
virtual void ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback) = 0;
|
||||
virtual void ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
|
||||
// Maintaining backwards API compatibility - ExtractArchiveBlocking below passes in extractWithRoot as an option
|
||||
virtual void ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
|
||||
virtual ~ArchiveCommands() = default;
|
||||
|
||||
//! Start a sync task to extract an archive to the target directory
|
||||
//! If you do not want to extract the root folder then set extractWithRootDirectory to false.
|
||||
virtual bool ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory) = 0;
|
||||
//! Create an archive of the target directory (all files and subdirectories)
|
||||
//! @param archivePath The path of the archive to create
|
||||
//! @dirToArchive The directory to be added to the archive
|
||||
//! @return Future (bool) which can obtain the success value of the operation
|
||||
[[nodiscard]] virtual std::future<bool> CreateArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& dirToArchive) = 0;
|
||||
|
||||
//! Extract a single file asynchronously from the archive to the destination.
|
||||
//! Uses cwd if destinationPath empty. overWrite = true for overwrite existing files, false for skipExisting
|
||||
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
|
||||
//! Multiple tasks can be associated with the same handle
|
||||
virtual void ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
|
||||
//! Extract an archive to the target directory
|
||||
//! @param archivePath The path of the archive to extract
|
||||
//! @param destinationPath The directory where files will be extracted to
|
||||
//! @return Future (bool) which can obtain the success value of the operation
|
||||
[[nodiscard]] virtual std::future<bool> ExtractArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& destinationPath) = 0;
|
||||
|
||||
//! Extract a single file from the archive to the destination and block until finished.
|
||||
//! Uses cwd if destinationPath empty. overWrite = true for overwrite existing files, false for skipExisting
|
||||
virtual bool ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite) = 0;
|
||||
//! Extract a single file from the archive to the destination
|
||||
//! Destination path should not be empty
|
||||
//! @param archivePath The path of the archive to extract from
|
||||
//! @param fileInArchive A path to a file, relative to root of archive
|
||||
//! @param destinationPath The directory where file will be extracted to
|
||||
//! @return Future (bool) which can obtain the success value of the operation
|
||||
[[nodiscard]] virtual std::future<bool> ExtractFile(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& fileInArchive,
|
||||
const AZStd::string& destinationPath) = 0;
|
||||
|
||||
//! Start an async task to create an archive of the target directory (recursively)
|
||||
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
|
||||
//! Multiple tasks can be associated with the same handle.
|
||||
virtual void CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
|
||||
//! Retrieve the list of files contained in an archive (all files and subdirectories)
|
||||
//! @param archivePath The path of the archive to list
|
||||
//! @param outFileEntries An out parameter that will contain the file paths found
|
||||
//! @return True if successful, false otherwise
|
||||
virtual bool ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& outFileEntries) = 0;
|
||||
|
||||
//! Start a sync task to create an archive of the target directory (recursively)
|
||||
virtual bool CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive) = 0;
|
||||
|
||||
//! Start an async task to retrieve the list of files and their relative paths within an archive (recursively)
|
||||
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
|
||||
//! Multiple tasks can be associated with the same handle.
|
||||
virtual void ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
|
||||
//! Add a file to an archive
|
||||
//! The archive might not exist yet
|
||||
//! The file path relative to the working directory will be replicated in the archive
|
||||
//! @param archivePath The path of the archive to add to
|
||||
//! @param workingDirectory A directory that will be the starting path of the file to be added
|
||||
//! @param fileToAdd A path to the file relative to the working directory
|
||||
//! @return Future (bool) which can obtain the success value of the operation
|
||||
[[nodiscard]] virtual std::future<bool> AddFileToArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& workingDirectory,
|
||||
const AZStd::string& fileToAdd) = 0;
|
||||
|
||||
//! Start a sync task to retrieve the list of files and their relative paths within an archive (recursively)
|
||||
virtual bool ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries) = 0;
|
||||
|
||||
//! Start an async task to add a file to a preexisting archive.
|
||||
//! fileToAdd must be a relative path to the file from the working directory. The path to the file from the root of the archive will be the same as the relative path to the file on disk.
|
||||
//! taskHandles are used to cancel a task at some point in the future and are provided by the caller per task.
|
||||
//! Multiple tasks can be associated with the same handle.
|
||||
virtual void AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
|
||||
|
||||
//! Start a sync task to add a file to a preexisting archive.
|
||||
//! fileToAdd must be a relative path to the file from the working directory. The path to the file from the root of the archive will be the same as the relative path to the file on disk.
|
||||
virtual bool AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd) = 0;
|
||||
|
||||
//! Start an async task to add files to a archive.
|
||||
//! File paths inside the list file must either be a relative path from the working directory or an absolute path.
|
||||
virtual void AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) = 0;
|
||||
|
||||
//! Start a sync task to add files to an archive.
|
||||
//! File paths inside the list file must either be a relative path from the working directory or an absolute path.
|
||||
virtual bool AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath) = 0;
|
||||
|
||||
//! Cancels tasks associtated with the given handle. Blocks until all tasks are cancelled.
|
||||
virtual void CancelTasks(AZ::Uuid taskHandle) = 0;
|
||||
//! Add files to an archive provided from a file listing
|
||||
//! The archive might not exist yet
|
||||
//! File paths in the file list should be relative to root of the archive
|
||||
//! @param archivePath The path of the archive to add to
|
||||
//! @param workingDirectory A directory that will be the starting path of the list of files to add
|
||||
//! @param listFilePath Full path to a text file that contains the list of files to add
|
||||
//! @return Future (bool) which can obtain the success value of the operation
|
||||
[[nodiscard]] virtual std::future<bool> AddFilesToArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& workingDirectory,
|
||||
const AZStd::string& listFilePath) = 0;
|
||||
};
|
||||
|
||||
using ArchiveCommandsBus = AZ::EBus<ArchiveCommands>;
|
||||
|
||||
}; // namespace AzToolsFramework
|
||||
|
||||
@@ -12,118 +12,98 @@
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
#include <AzFramework/Archive/INestedArchive.h>
|
||||
#include <AzFramework/Archive/ZipDirStructures.h>
|
||||
#include <AzFramework/Process/ProcessCommunicator.h>
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
#include <AzFramework/FileFunc/FileFunc.h>
|
||||
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
// Forward declare platform specific functions
|
||||
namespace Platform
|
||||
constexpr const char s_traceName[] = "ArchiveComponent";
|
||||
constexpr AZ::u32 s_compressionMethod = AZ::IO::INestedArchive::METHOD_DEFLATE;
|
||||
constexpr AZ::s32 s_compressionLevel = AZ::IO::INestedArchive::LEVEL_NORMAL;
|
||||
constexpr CompressionCodec::Codec s_compressionCodec = CompressionCodec::Codec::ZLIB;
|
||||
|
||||
namespace ArchiveUtils
|
||||
{
|
||||
AZStd::string GetZipExePath();
|
||||
AZStd::string GetUnzipExePath();
|
||||
|
||||
AZStd::string GetCreateArchiveCommand(const AZStd::string& archivePath, const AZStd::string& dirToArchive);
|
||||
AZStd::string GetExtractArchiveCommand(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool includeRoot);
|
||||
AZStd::string GetAddFileToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& file);
|
||||
AZStd::string GetAddFilesToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& listFilePath);
|
||||
AZStd::string GetExtractFileCommand(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite);
|
||||
AZStd::string GetListFilesInArchiveCommand(const AZStd::string& archivePath);
|
||||
void ParseConsoleOutputFromListFilesInArchive(const AZStd::string& consoleOutput, AZStd::vector<AZStd::string>& fileEntries);
|
||||
}
|
||||
|
||||
const char s_traceName[] = "ArchiveComponent";
|
||||
const unsigned int g_sleepDuration = 1;
|
||||
|
||||
// Echoes all results of stdout and stderr to console and never blocks
|
||||
class ConsoleEchoCommunicator
|
||||
{
|
||||
public:
|
||||
ConsoleEchoCommunicator(AzFramework::ProcessCommunicator* communicator)
|
||||
: m_communicator(communicator)
|
||||
// Read a file's contents into a provided buffer.
|
||||
// Does not add a zero byte at the end of the buffer.
|
||||
// returns true if read was successful, false otherwise.
|
||||
bool ReadFile(const AZ::IO::Path& filePath, AZ::IO::OpenMode openMode, AZStd::vector<char>& outBuffer)
|
||||
{
|
||||
}
|
||||
|
||||
~ConsoleEchoCommunicator()
|
||||
{
|
||||
}
|
||||
|
||||
// Call this periodically to drain the buffers
|
||||
void Pump()
|
||||
{
|
||||
if (m_communicator->IsValid())
|
||||
auto fileIO = AZ::IO::FileIOBase::GetDirectInstance();
|
||||
if (!fileIO)
|
||||
{
|
||||
AZ::u32 readBufferSize = 0;
|
||||
AZStd::string readBuffer;
|
||||
// Don't call readOutput unless there is output or else it will block...
|
||||
readBufferSize = m_communicator->PeekOutput();
|
||||
if (readBufferSize)
|
||||
{
|
||||
readBuffer.resize_no_construct(readBufferSize + 1);
|
||||
readBuffer[readBufferSize] = '\0';
|
||||
m_communicator->ReadOutput(readBuffer.data(), readBufferSize);
|
||||
EchoBuffer(readBuffer);
|
||||
}
|
||||
readBufferSize = m_communicator->PeekError();
|
||||
if (readBufferSize)
|
||||
{
|
||||
readBuffer.resize_no_construct(readBufferSize + 1);
|
||||
readBuffer[readBufferSize] = '\0';
|
||||
m_communicator->ReadError(readBuffer.data(), readBufferSize);
|
||||
EchoBuffer(readBuffer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void EchoBuffer(const AZStd::string& buffer)
|
||||
{
|
||||
size_t startIndex = 0;
|
||||
size_t endIndex = 0;
|
||||
const size_t bufferSize = buffer.size();
|
||||
for (size_t i = 0; i < bufferSize; ++i)
|
||||
bool success = false;
|
||||
AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
|
||||
if (fileIO->Open(filePath.c_str(), openMode, fileHandle))
|
||||
{
|
||||
if (buffer[i] == '\n' || buffer[i] == '\0')
|
||||
AZ::u64 fileSize = 0;
|
||||
if (fileIO->Size(fileHandle, fileSize) && fileSize != 0)
|
||||
{
|
||||
endIndex = i;
|
||||
bool isEmptyMessage = (endIndex - startIndex == 1) && (buffer[startIndex] == '\r');
|
||||
if (!isEmptyMessage)
|
||||
outBuffer.resize_no_construct(fileSize);
|
||||
|
||||
AZ::u64 bytesRead = 0;
|
||||
if (fileIO->Read(fileHandle, outBuffer.data(), fileSize, true, &bytesRead))
|
||||
{
|
||||
AZ_Printf(s_traceName, "%s", buffer.substr(startIndex, endIndex - startIndex).c_str());
|
||||
success = (fileSize == bytesRead);
|
||||
}
|
||||
startIndex = endIndex + 1;
|
||||
}
|
||||
|
||||
fileIO->Close(fileHandle);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Reads a text file that contains a list of file paths.
|
||||
// Tokenize the file by lines.
|
||||
// Calls the lineVisitor function for each line of the file.
|
||||
void ProcessFileList(const AZ::IO::Path& filePath, AZStd::function<void(AZStd::string_view line)> lineVisitor)
|
||||
{
|
||||
AZStd::vector<char> fileBuffer;
|
||||
if (ReadFile(filePath, AZ::IO::OpenMode::ModeText | AZ::IO::OpenMode::ModeRead, fileBuffer))
|
||||
{
|
||||
AZ::StringFunc::TokenizeVisitor(AZStd::string_view{ fileBuffer.data(), fileBuffer.size() }, lineVisitor, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
AzFramework::ProcessCommunicator* m_communicator = nullptr;
|
||||
};
|
||||
} // namespace ArchiveUtils
|
||||
|
||||
void ArchiveComponent::Activate()
|
||||
{
|
||||
m_zipExePath = Platform::GetZipExePath();
|
||||
m_unzipExePath = Platform::GetUnzipExePath();
|
||||
m_fileIO = AZ::IO::FileIOBase::GetDirectInstance();
|
||||
if (m_fileIO == nullptr)
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to create a LocalFileIO instance!");
|
||||
}
|
||||
|
||||
ArchiveCommands::Bus::Handler::BusConnect();
|
||||
m_archive = AZ::Interface<AZ::IO::IArchive>::Get();
|
||||
if (m_archive == nullptr)
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to get IArchive interface!");
|
||||
}
|
||||
|
||||
ArchiveCommandsBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void ArchiveComponent::Deactivate()
|
||||
{
|
||||
ArchiveCommands::Bus::Handler::BusDisconnect();
|
||||
ArchiveCommandsBus::Handler::BusDisconnect();
|
||||
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
|
||||
for (auto pair : m_threadInfoMap)
|
||||
m_fileIO = nullptr;
|
||||
m_archive = nullptr;
|
||||
|
||||
for (AZStd::thread& t : m_threads)
|
||||
{
|
||||
ThreadInfo& info = pair.second;
|
||||
info.shouldStop = true;
|
||||
m_cv.wait(lock, [&info]() {
|
||||
return info.threads.size() == 0;
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
m_threadInfoMap.clear();
|
||||
m_threads = {};
|
||||
}
|
||||
|
||||
void ArchiveComponent::Reflect(AZ::ReflectContext * context)
|
||||
@@ -132,7 +112,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
serializeContext->Class<ArchiveComponent, AZ::Component>()
|
||||
->Version(2)
|
||||
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AZ_CRC("AssetBuilder", 0xc739c7d7) }))
|
||||
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AZ_CRC_CE("AssetBuilder") }))
|
||||
;
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
@@ -141,320 +121,480 @@ namespace AzToolsFramework
|
||||
"Archive", "Handles creation and extraction of zip archives.")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Editor")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveComponent::CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
|
||||
std::future<bool> ArchiveComponent::CreateArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& dirToArchive)
|
||||
{
|
||||
AZStd::string commandLineArgs = AZStd::string::format(R"(a -tzip -mx=1 "%s" -r "%s\*")", archivePath.c_str(), dirToArchive.c_str());
|
||||
LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle);
|
||||
}
|
||||
|
||||
bool ArchiveComponent::CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive)
|
||||
{
|
||||
bool success = false;
|
||||
auto createArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
|
||||
success = result;
|
||||
};
|
||||
|
||||
AZStd::string commandLineArgs = Platform::GetCreateArchiveCommand(archivePath, dirToArchive);
|
||||
|
||||
if (commandLineArgs.empty())
|
||||
if (!CheckParamsForCreate(archivePath, dirToArchive))
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return false;
|
||||
std::promise<bool> p;
|
||||
p.set_value(false);
|
||||
return p.get_future();
|
||||
}
|
||||
|
||||
LaunchZipExe(m_zipExePath, commandLineArgs, createArchiveCallback, AZ::Uuid::CreateNull(), dirToArchive, false);
|
||||
return success;
|
||||
}
|
||||
|
||||
void ArchiveComponent::ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback)
|
||||
{
|
||||
ArchiveResponseOutputCallback responseHandler = [respCallback](bool result, AZStd::string /*outputStr*/) { respCallback(result); };
|
||||
ExtractArchiveOutput(archivePath, destinationPath, taskHandle, responseHandler);
|
||||
}
|
||||
|
||||
void ArchiveComponent::ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, true);
|
||||
|
||||
if (commandLineArgs.empty())
|
||||
auto FnCreateArchive = [this, archivePath, dirToArchive](std::promise<bool>&& p) -> void
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return;
|
||||
}
|
||||
|
||||
LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
|
||||
}
|
||||
|
||||
void ArchiveComponent::ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, false);
|
||||
|
||||
if (commandLineArgs.empty())
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return;
|
||||
}
|
||||
|
||||
LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
|
||||
}
|
||||
|
||||
void ArchiveComponent::ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
AZStd::string commandLineArgs = AzToolsFramework::Platform::GetExtractFileCommand(archivePath, fileInArchive, destinationPath, overWrite);
|
||||
if (commandLineArgs.empty())
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return;
|
||||
}
|
||||
LaunchZipExe(m_unzipExePath, commandLineArgs, respCallback, taskHandle);
|
||||
}
|
||||
|
||||
bool ArchiveComponent::ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite)
|
||||
{
|
||||
AZStd::string commandLineArgs = AzToolsFramework::Platform::GetExtractFileCommand(archivePath, fileInArchive, destinationPath, overWrite);
|
||||
if (commandLineArgs.empty())
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
auto createArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
|
||||
success = result;
|
||||
};
|
||||
LaunchZipExe(m_unzipExePath, commandLineArgs, createArchiveCallback);
|
||||
return success;
|
||||
}
|
||||
|
||||
void ArchiveComponent::ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
AZStd::string commandLineArgs = Platform::GetListFilesInArchiveCommand(archivePath);
|
||||
|
||||
auto parseOutput = [respCallback, &fileEntries](bool exitCode, AZStd::string consoleOutput)
|
||||
{
|
||||
Platform::ParseConsoleOutputFromListFilesInArchive(consoleOutput, fileEntries);
|
||||
AZ::TickBus::QueueFunction(respCallback, exitCode, AZStd::move(consoleOutput));
|
||||
};
|
||||
LaunchZipExe(m_unzipExePath, commandLineArgs, parseOutput, taskHandle, "", true);
|
||||
}
|
||||
|
||||
bool ArchiveComponent::ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries)
|
||||
{
|
||||
AZStd::string listOutput;
|
||||
AZStd::string commandLineArgs = Platform::GetListFilesInArchiveCommand(archivePath.c_str());
|
||||
bool success = false;
|
||||
|
||||
auto parseOutput = [&success, &fileEntries](bool result, AZStd::string consoleOutput)
|
||||
{
|
||||
Platform::ParseConsoleOutputFromListFilesInArchive(consoleOutput, fileEntries);
|
||||
success = result;
|
||||
};
|
||||
LaunchZipExe(m_unzipExePath, commandLineArgs, parseOutput, AZ::Uuid::CreateNull(), "", true);
|
||||
return success;
|
||||
}
|
||||
|
||||
void ArchiveComponent::AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
AZStd::string commandLineArgs = Platform::GetAddFileToArchiveCommand(archivePath, fileToAdd);
|
||||
if (commandLineArgs.empty())
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return;
|
||||
}
|
||||
|
||||
LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle, workingDirectory);
|
||||
}
|
||||
|
||||
bool ArchiveComponent::AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd)
|
||||
{
|
||||
AZStd::string commandLineArgs = Platform::GetAddFileToArchiveCommand(archivePath, fileToAdd);
|
||||
if (commandLineArgs.empty())
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
auto addFileToArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
|
||||
success = result;
|
||||
};
|
||||
|
||||
LaunchZipExe(m_zipExePath, commandLineArgs, addFileToArchiveCallback, AZ::Uuid::CreateNull(), workingDirectory);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ArchiveComponent::AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath)
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
auto addFileToArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
|
||||
success = result;
|
||||
};
|
||||
|
||||
AZStd::string commandLineArgs = Platform::GetAddFilesToArchiveCommand(archivePath.c_str(), listFilePath.c_str());
|
||||
|
||||
if (commandLineArgs.empty())
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return false;
|
||||
}
|
||||
LaunchZipExe(m_zipExePath, commandLineArgs, addFileToArchiveCallback, AZ::Uuid::CreateNull(), workingDirectory);
|
||||
return success;
|
||||
}
|
||||
|
||||
void ArchiveComponent::AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
AZStd::string commandLineArgs = Platform::GetAddFilesToArchiveCommand(archivePath, listFilePath);
|
||||
if (commandLineArgs.empty())
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return;
|
||||
}
|
||||
|
||||
LaunchZipExe(m_zipExePath, commandLineArgs, respCallback, taskHandle, workingDirectory);
|
||||
}
|
||||
|
||||
|
||||
bool ArchiveComponent::ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory)
|
||||
{
|
||||
AZStd::string commandLineArgs = Platform::GetExtractArchiveCommand(archivePath, destinationPath, extractWithRootDirectory);
|
||||
|
||||
if (commandLineArgs.empty())
|
||||
{
|
||||
// The platform-specific implementation has already thrown its own error, no need to throw another one
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
auto extractArchiveCallback = [&success](bool result, AZStd::string consoleOutput) {
|
||||
success = result;
|
||||
};
|
||||
|
||||
LaunchZipExe(m_unzipExePath, commandLineArgs, extractArchiveCallback);
|
||||
return success;
|
||||
}
|
||||
|
||||
void ArchiveComponent::CancelTasks(AZ::Uuid taskHandle)
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
|
||||
|
||||
auto it = m_threadInfoMap.find(taskHandle);
|
||||
if (it == m_threadInfoMap.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadInfo& info = it->second;
|
||||
info.shouldStop = true;
|
||||
m_cv.wait(lock, [&info]() {
|
||||
return info.threads.size() == 0;
|
||||
});
|
||||
m_threadInfoMap.erase(it);
|
||||
}
|
||||
|
||||
void ArchiveComponent::LaunchZipExe(const AZStd::string& exePath, const AZStd::string& commandLineArgs, const ArchiveResponseOutputCallback& respCallback, AZ::Uuid taskHandle, const AZStd::string& workingDir, bool captureOutput)
|
||||
{
|
||||
auto sevenZJob = [=]()
|
||||
{
|
||||
if (!taskHandle.IsNull())
|
||||
auto archive = m_archive->OpenArchive(archivePath, {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
|
||||
if (!archive)
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
|
||||
m_threadInfoMap[taskHandle].threads.insert(AZStd::this_thread::get_id());
|
||||
m_cv.notify_all();
|
||||
AZ_Error(s_traceName, false, "Failed to create archive file '%s'", archivePath.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo info;
|
||||
info.m_commandlineParameters = exePath + " " + commandLineArgs;
|
||||
|
||||
info.m_showWindow = false;
|
||||
if (!workingDir.empty())
|
||||
auto foundFiles = AzFramework::FileFunc::FindFilesInPath(dirToArchive, "*", true);
|
||||
if (!foundFiles.IsSuccess())
|
||||
{
|
||||
info.m_workingDirectory = workingDir;
|
||||
AZ_Error(s_traceName, false, "Failed to find file listing under directory '%d'", dirToArchive.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
AZStd::unique_ptr<AzFramework::ProcessWatcher> watcher(AzFramework::ProcessWatcher::LaunchProcess(info, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT));
|
||||
|
||||
AZStd::string consoleOutput;
|
||||
AZ::u32 exitCode = static_cast<AZ::u32>(SevenZipExitCode::UserStoppedProcess);
|
||||
if (watcher)
|
||||
bool success = true;
|
||||
AZStd::vector<char> fileBuffer;
|
||||
const AZ::IO::Path workingPath{ dirToArchive };
|
||||
|
||||
for (const auto& fileName : foundFiles.GetValue())
|
||||
{
|
||||
// callback requires output captured from 7z
|
||||
if (captureOutput)
|
||||
bool thisSuccess = false;
|
||||
|
||||
AZ::IO::PathView relativePath = AZ::IO::PathView{ fileName }.LexicallyRelative(workingPath);
|
||||
|
||||
AZ::IO::Path fullPath = (workingPath / relativePath);
|
||||
if (ArchiveUtils::ReadFile(fullPath, AZ::IO::OpenMode::ModeRead, fileBuffer))
|
||||
{
|
||||
AZStd::string consoleBuffer;
|
||||
while (watcher->IsProcessRunning(&exitCode))
|
||||
{
|
||||
if (!taskHandle.IsNull())
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
|
||||
if (m_threadInfoMap[taskHandle].shouldStop)
|
||||
{
|
||||
watcher->TerminateProcess(static_cast<AZ::u32>(SevenZipExitCode::UserStoppedProcess));
|
||||
}
|
||||
}
|
||||
watcher->WaitForProcessToExit(g_sleepDuration, &exitCode);
|
||||
AZ::u32 outputSize = watcher->GetCommunicator()->PeekOutput();
|
||||
if (outputSize)
|
||||
{
|
||||
consoleBuffer.resize(outputSize);
|
||||
watcher->GetCommunicator()->ReadOutput(consoleBuffer.data(), outputSize);
|
||||
consoleOutput += consoleBuffer;
|
||||
}
|
||||
}
|
||||
int result = archive->UpdateFile(
|
||||
relativePath.Native(), fileBuffer.data(), fileBuffer.size(), s_compressionMethod,
|
||||
s_compressionLevel, s_compressionCodec);
|
||||
|
||||
thisSuccess = (result == AZ::IO::ZipDir::ZD_ERROR_SUCCESS);
|
||||
AZ_Error(
|
||||
s_traceName, thisSuccess, "Error %d encountered while adding '%s' to archive '%.*s'", result, fileName.c_str(),
|
||||
AZ_STRING_ARG(archive->GetFullPath().Native()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleEchoCommunicator echoCommunicator(watcher->GetCommunicator());
|
||||
while (watcher->IsProcessRunning(&exitCode))
|
||||
{
|
||||
if (!taskHandle.IsNull())
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
|
||||
if (m_threadInfoMap[taskHandle].shouldStop)
|
||||
{
|
||||
watcher->TerminateProcess(static_cast<AZ::u32>(SevenZipExitCode::UserStoppedProcess));
|
||||
}
|
||||
}
|
||||
watcher->WaitForProcessToExit(g_sleepDuration, &exitCode);
|
||||
echoCommunicator.Pump();
|
||||
}
|
||||
AZ_Error(
|
||||
s_traceName, false, "Error encountered while reading '%s' to add to archive '%.*s'", fileName.c_str(),
|
||||
AZ_STRING_ARG(archive->GetFullPath().Native()));
|
||||
}
|
||||
|
||||
success = (success && thisSuccess);
|
||||
}
|
||||
|
||||
if (taskHandle.IsNull())
|
||||
archive.reset();
|
||||
p.set_value(success);
|
||||
};
|
||||
|
||||
// Async task...
|
||||
std::promise<bool> p;
|
||||
std::future<bool> f = p.get_future();
|
||||
|
||||
AZStd::thread_desc threadDesc;
|
||||
threadDesc.m_name = "Archive Task (Create)";
|
||||
m_threads.emplace_back(threadDesc, FnCreateArchive, AZStd::move(p));
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
std::future<bool> ArchiveComponent::ExtractArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& destinationPath)
|
||||
{
|
||||
if (!CheckParamsForExtract(archivePath, destinationPath))
|
||||
{
|
||||
std::promise<bool> p;
|
||||
p.set_value(false);
|
||||
return p.get_future();
|
||||
}
|
||||
|
||||
auto FnExtractArchive = [this, archivePath, destinationPath](std::promise<bool>&& p) -> void
|
||||
{
|
||||
auto archive = m_archive->OpenArchive(archivePath, {}, AZ::IO::INestedArchive::FLAGS_READ_ONLY);
|
||||
if (!archive)
|
||||
{
|
||||
respCallback(exitCode == static_cast<AZ::u32>(SevenZipExitCode::NoError), AZStd::move(consoleOutput));
|
||||
AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::IO::Path> filesInArchive;
|
||||
if (int result = archive->ListAllFiles(filesInArchive); result != AZ::IO::ZipDir::ZD_ERROR_SUCCESS)
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to get list of files in archive '%s'", archivePath.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::u8> fileBuffer;
|
||||
AZ::IO::Path destination{ destinationPath };
|
||||
AZ::u64 fileSize = 0;
|
||||
AZ::u64 numFilesWritten = 0;
|
||||
AZ::u64 bytesWritten = 0;
|
||||
AZ::IO::INestedArchive::Handle srcHandle{};
|
||||
AZ::IO::HandleType dstHandle = AZ::IO::InvalidHandle;
|
||||
constexpr AZ::IO::OpenMode openMode =
|
||||
(AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeUpdate);
|
||||
|
||||
for (const auto& filePath : filesInArchive)
|
||||
{
|
||||
srcHandle = archive->FindFile(filePath.Native());
|
||||
AZ_Assert(srcHandle != nullptr, "File '%s' does not exist inside archive '%s'", filePath.c_str(), archivePath.c_str());
|
||||
|
||||
fileSize = (srcHandle != nullptr) ? archive->GetFileSize(srcHandle) : 0;
|
||||
fileBuffer.resize_no_construct(fileSize);
|
||||
if (auto result = archive->ReadFile(srcHandle, fileBuffer.data()); result != AZ::IO::ZipDir::ZD_ERROR_SUCCESS)
|
||||
{
|
||||
AZ_Error(
|
||||
s_traceName, false, "Failed to read file '%s' in archive '%s' with error %d", filePath.c_str(), archivePath.c_str(),
|
||||
result);
|
||||
continue;
|
||||
}
|
||||
|
||||
AZ::IO::Path destinationFile = destination / filePath;
|
||||
if (!m_fileIO->Open(destinationFile.c_str(), openMode, dstHandle))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to open '%s' for writing", destinationFile.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m_fileIO->Write(dstHandle, fileBuffer.data(), fileSize, &bytesWritten))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to write destination file '%s'", destinationFile.c_str());
|
||||
}
|
||||
else if (bytesWritten == fileSize)
|
||||
{
|
||||
++numFilesWritten;
|
||||
}
|
||||
|
||||
m_fileIO->Close(dstHandle);
|
||||
}
|
||||
|
||||
p.set_value(numFilesWritten == filesInArchive.size());
|
||||
};
|
||||
|
||||
// Async task...
|
||||
std::promise<bool> p;
|
||||
std::future<bool> f = p.get_future();
|
||||
|
||||
AZStd::thread_desc threadDesc;
|
||||
threadDesc.m_name = "Archive Task (Extract)";
|
||||
m_threads.emplace_back(threadDesc, FnExtractArchive, AZStd::move(p));
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
std::future<bool> ArchiveComponent::ExtractFile(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& fileInArchive,
|
||||
const AZStd::string& destinationPath)
|
||||
{
|
||||
if (!CheckParamsForExtract(archivePath, destinationPath))
|
||||
{
|
||||
std::promise<bool> p;
|
||||
p.set_value(false);
|
||||
return p.get_future();
|
||||
}
|
||||
|
||||
auto FnExtractFile = [this, archivePath, fileInArchive, destinationPath](std::promise<bool>&& p) -> void
|
||||
{
|
||||
auto archive = m_archive->OpenArchive(archivePath, {}, AZ::IO::INestedArchive::FLAGS_READ_ONLY);
|
||||
if (!archive)
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::IO::INestedArchive::Handle fileHandle = archive->FindFile(fileInArchive);
|
||||
if (!fileHandle)
|
||||
{
|
||||
AZ_Error(s_traceName, false, "File '%s' does not exist inside archive '%s'", fileInArchive.c_str(), archivePath.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::u64 fileSize = archive->GetFileSize(fileHandle);
|
||||
AZStd::vector<AZ::u8> fileBuffer;
|
||||
fileBuffer.resize_no_construct(fileSize);
|
||||
|
||||
if (auto result = archive->ReadFile(fileHandle, fileBuffer.data()); result != AZ::IO::ZipDir::ZD_ERROR_SUCCESS)
|
||||
{
|
||||
AZ_Error(
|
||||
s_traceName, false, "Failed to read file '%s' in archive '%s' with error %d", fileInArchive.c_str(),
|
||||
archivePath.c_str(), result);
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::IO::HandleType destFileHandle = AZ::IO::InvalidHandle;
|
||||
AZ::IO::Path destinationFile{ destinationPath };
|
||||
destinationFile /= fileInArchive;
|
||||
AZ::IO::OpenMode openMode = (AZ::IO::OpenMode::ModeCreatePath | AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeUpdate);
|
||||
if (!m_fileIO->Open(destinationFile.c_str(), openMode, destFileHandle))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to open destination file '%s' for writing", destinationFile.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::u64 bytesWritten = 0;
|
||||
if (!m_fileIO->Write(destFileHandle, fileBuffer.data(), fileSize, &bytesWritten))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to write destination file '%s'", destinationFile.c_str());
|
||||
}
|
||||
|
||||
m_fileIO->Close(destFileHandle);
|
||||
p.set_value(bytesWritten == fileSize);
|
||||
};
|
||||
|
||||
// Async task...
|
||||
std::promise<bool> p;
|
||||
std::future<bool> f = p.get_future();
|
||||
|
||||
AZStd::thread_desc threadDesc;
|
||||
threadDesc.m_name = "Archive Task (Extract Single)";
|
||||
m_threads.emplace_back(threadDesc, FnExtractFile, AZStd::move(p));
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
bool ArchiveComponent::ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& outFileEntries)
|
||||
{
|
||||
if (!m_fileIO || !m_archive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_fileIO->Exists(archivePath.c_str()))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Archive '%s' does not exist!", archivePath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto archive = m_archive->OpenArchive(archivePath, {}, AZ::IO::INestedArchive::FLAGS_READ_ONLY);
|
||||
if (!archive)
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::IO::Path> fileEntries;
|
||||
int result = archive->ListAllFiles(fileEntries);
|
||||
outFileEntries.clear();
|
||||
for (const auto& path : fileEntries)
|
||||
{
|
||||
outFileEntries.emplace_back(path.String());
|
||||
}
|
||||
return (result == AZ::IO::ZipDir::ZD_ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
std::future<bool> ArchiveComponent::AddFileToArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& workingDirectory,
|
||||
const AZStd::string& fileToAdd)
|
||||
{
|
||||
if (!CheckParamsForAdd(workingDirectory, fileToAdd))
|
||||
{
|
||||
std::promise<bool> p;
|
||||
p.set_value(false);
|
||||
return p.get_future();
|
||||
}
|
||||
|
||||
auto FnAddFileToArchive = [this, archivePath, workingDirectory, fileToAdd](std::promise<bool>&& p) -> void
|
||||
{
|
||||
auto archive = m_archive->OpenArchive(archivePath);
|
||||
if (!archive)
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::IO::Path workingPath{ workingDirectory };
|
||||
AZ::IO::Path fullPath = workingPath / fileToAdd;
|
||||
AZ::IO::PathView relativePath = AZ::IO::PathView{ fullPath }.LexicallyRelative(workingPath);
|
||||
|
||||
AZStd::vector<char> fileBuffer;
|
||||
bool success = false;
|
||||
if (ArchiveUtils::ReadFile(fullPath, AZ::IO::OpenMode::ModeRead, fileBuffer))
|
||||
{
|
||||
int result = archive->UpdateFile(
|
||||
relativePath.Native(), fileBuffer.data(), fileBuffer.size(), s_compressionMethod,
|
||||
s_compressionLevel, s_compressionCodec);
|
||||
|
||||
success = (result == AZ::IO::ZipDir::ZD_ERROR_SUCCESS);
|
||||
AZ_Error(
|
||||
s_traceName, success, "Error %d encountered while adding '%s' to archive '%.*s'", result, fileToAdd.c_str(),
|
||||
AZ_STRING_ARG(archive->GetFullPath().Native()));
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::TickBus::QueueFunction(respCallback, (exitCode == static_cast<AZ::u32>(SevenZipExitCode::NoError)), AZStd::move(consoleOutput));
|
||||
AZ_Error(
|
||||
s_traceName, false, "Error encountered while reading '%s' to add to archive '%.*s'", fileToAdd.c_str(),
|
||||
AZ_STRING_ARG(archive->GetFullPath().Native()));
|
||||
}
|
||||
|
||||
if (!taskHandle.IsNull())
|
||||
{
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
|
||||
ThreadInfo& tInfo = m_threadInfoMap[taskHandle];
|
||||
tInfo.threads.erase(AZStd::this_thread::get_id());
|
||||
m_cv.notify_all();
|
||||
}
|
||||
archive.reset();
|
||||
p.set_value(success);
|
||||
};
|
||||
if (!taskHandle.IsNull())
|
||||
{
|
||||
AZStd::thread processThread(sevenZJob);
|
||||
AZStd::unique_lock<AZStd::mutex> lock(m_threadControlMutex);
|
||||
ThreadInfo& info = m_threadInfoMap[taskHandle];
|
||||
m_cv.wait(lock, [&info, &processThread]() {
|
||||
return info.threads.find(processThread.get_id()) != info.threads.end();
|
||||
});
|
||||
processThread.detach();
|
||||
}
|
||||
else
|
||||
{
|
||||
sevenZJob();
|
||||
}
|
||||
|
||||
// Async task...
|
||||
std::promise<bool> p;
|
||||
std::future<bool> f = p.get_future();
|
||||
|
||||
AZStd::thread_desc threadDesc;
|
||||
threadDesc.m_name = "Archive Task (Add Single)";
|
||||
m_threads.emplace_back(threadDesc, FnAddFileToArchive, AZStd::move(p));
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
std::future<bool> ArchiveComponent::AddFilesToArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& workingDirectory,
|
||||
const AZStd::string& listFilePath)
|
||||
{
|
||||
if (!CheckParamsForAdd(workingDirectory, listFilePath))
|
||||
{
|
||||
std::promise<bool> p;
|
||||
p.set_value(false);
|
||||
return p.get_future();
|
||||
}
|
||||
|
||||
auto FnAddFilesToArchive = [this, archivePath, workingDirectory, listFilePath](std::promise<bool>&& p) -> void
|
||||
{
|
||||
auto archive = m_archive->OpenArchive(archivePath);
|
||||
if (!archive)
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to open archive file '%s'", archivePath.c_str());
|
||||
p.set_value(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = true; // starts true and turns false when any error is encountered.
|
||||
AZ::IO::Path basePath{ workingDirectory };
|
||||
|
||||
auto PerLineCallback = [&success, &basePath, &archive](AZStd::string_view filePathLine) -> void
|
||||
{
|
||||
AZStd::vector<char> fileBuffer;
|
||||
AZ::IO::Path fullPath = (basePath / filePathLine);
|
||||
if (ArchiveUtils::ReadFile(fullPath, AZ::IO::OpenMode::ModeRead, fileBuffer))
|
||||
{
|
||||
int result = archive->UpdateFile(
|
||||
filePathLine, fileBuffer.data(), fileBuffer.size(), s_compressionMethod,
|
||||
s_compressionLevel, s_compressionCodec);
|
||||
|
||||
bool thisSuccess = (result == AZ::IO::ZipDir::ZD_ERROR_SUCCESS);
|
||||
success = (success && thisSuccess);
|
||||
AZ_Error(
|
||||
s_traceName, thisSuccess, "Error %d encountered while adding '%.*s' to archive '%.*s'", result,
|
||||
AZ_STRING_ARG(filePathLine), AZ_STRING_ARG(archive->GetFullPath().Native()));
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(
|
||||
s_traceName, false, "Error encountered while reading '%.*s' to add to archive '%.*s'", AZ_STRING_ARG(filePathLine),
|
||||
AZ_STRING_ARG(archive->GetFullPath().Native()));
|
||||
}
|
||||
};
|
||||
|
||||
ArchiveUtils::ProcessFileList(listFilePath, PerLineCallback);
|
||||
|
||||
archive.reset();
|
||||
p.set_value(success);
|
||||
};
|
||||
|
||||
// Async task...
|
||||
std::promise<bool> p;
|
||||
std::future<bool> f = p.get_future();
|
||||
|
||||
AZStd::thread_desc threadDesc;
|
||||
threadDesc.m_name = "Archive Task (Add)";
|
||||
m_threads.emplace_back(threadDesc, FnAddFilesToArchive, AZStd::move(p));
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
bool ArchiveComponent::CheckParamsForAdd(const AZStd::string& directory, const AZStd::string& file)
|
||||
{
|
||||
if (!m_fileIO || !m_archive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_fileIO->IsDirectory(directory.c_str()))
|
||||
{
|
||||
AZ_Error(
|
||||
s_traceName, false, "Working directory '%s' is not a directory or doesn't exist!", directory.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file.empty())
|
||||
{
|
||||
auto filePath = AZ::IO::Path{ directory } / file;
|
||||
if (!m_fileIO->Exists(filePath.c_str()) || m_fileIO->IsDirectory(filePath.c_str()))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "File list '%s' is a directory or doesn't exist!", filePath.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArchiveComponent::CheckParamsForExtract(const AZStd::string& archive, const AZStd::string& directory)
|
||||
{
|
||||
if (!m_fileIO || !m_archive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_fileIO->Exists(archive.c_str()))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Archive '%s' does not exist!", archive.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_fileIO->Exists(directory.c_str()))
|
||||
{
|
||||
if (!m_fileIO->CreatePath(directory.c_str()))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Failed to create destination directory '%s'", directory.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArchiveComponent::CheckParamsForCreate(const AZStd::string& archive, const AZStd::string& directory)
|
||||
{
|
||||
if (!m_fileIO || !m_archive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_fileIO->Exists(archive.c_str()))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Archive file '%s' already exists, cannot create a new archive there!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_fileIO->IsDirectory(directory.c_str()))
|
||||
{
|
||||
AZ_Error(s_traceName, false, "Directory '%s' is not a directory or doesn't exist!", directory.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -10,80 +10,76 @@
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/std/parallel/thread.h>
|
||||
#include <AzCore/std/parallel/conditional_variable.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
|
||||
#include <AzFramework/Archive/IArchive.h>
|
||||
#include <AzToolsFramework/Archive/ArchiveAPI.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
enum class SevenZipExitCode : AZ::u32
|
||||
{
|
||||
NoError = 0,
|
||||
Warning = 1,
|
||||
FatalError = 2,
|
||||
CommandLineError = 7,
|
||||
NotEnoughMemory = 8,
|
||||
UserStoppedProcess = 255
|
||||
};
|
||||
|
||||
// the ArchiveComponent's job is to execute zip commands.
|
||||
// it parses the status of zip commands and returns results.
|
||||
// the ArchiveComponent's job is to create and manipulate zip archives.
|
||||
class ArchiveComponent
|
||||
: public AZ::Component
|
||||
, private ArchiveCommands::Bus::Handler
|
||||
, private ArchiveCommandsBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ArchiveComponent, "{A19EEA33-3736-447F-ACF7-DAA4B6A179AA}")
|
||||
AZ_COMPONENT(ArchiveComponent, "{A19EEA33-3736-447F-ACF7-DAA4B6A179AA}");
|
||||
|
||||
ArchiveComponent() = default;
|
||||
~ArchiveComponent() override = default;
|
||||
|
||||
ArchiveComponent(const ArchiveComponent&) = delete;
|
||||
ArchiveComponent& operator=(const ArchiveComponent&) = delete;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component overrides
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
private:
|
||||
|
||||
protected:
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ArchiveCommands::Bus::Handler overrides
|
||||
void CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
bool CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive) override;
|
||||
bool ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory) override;
|
||||
void ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback) override;
|
||||
void ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
void ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
void ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
bool ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite) override;
|
||||
void ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
bool ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& fileEntries) override;
|
||||
void AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
bool AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& fileToAdd) override;
|
||||
bool AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath) override;
|
||||
void AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
void CancelTasks(AZ::Uuid taskHandle) override;
|
||||
// ArchiveCommandsBus::Handler overrides
|
||||
[[nodiscard]] std::future<bool> CreateArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& dirToArchive) override;
|
||||
|
||||
[[nodiscard]] std::future<bool> ExtractArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& destinationPath) override;
|
||||
|
||||
[[nodiscard]] std::future<bool> ExtractFile(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& fileInArchive,
|
||||
const AZStd::string& destinationPath) override;
|
||||
|
||||
bool ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& outFileEntries) override;
|
||||
|
||||
[[nodiscard]] std::future<bool> AddFileToArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& workingDirectory,
|
||||
const AZStd::string& fileToAdd) override;
|
||||
|
||||
[[nodiscard]] std::future<bool> AddFilesToArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& workingDirectory,
|
||||
const AZStd::string& listFilePath) override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Launches the input zip exe as a background child process in a detached background thread, if the task handle is not null
|
||||
// otherwise launches input zip exe in the calling thread.
|
||||
void LaunchZipExe(const AZStd::string& exePath, const AZStd::string& commandLineArgs, const ArchiveResponseOutputCallback& respCallback, AZ::Uuid taskHandle = AZ::Uuid::CreateNull(), const AZStd::string& workingDir = "", bool captureOutput = false);
|
||||
|
||||
AZStd::string m_zipExePath;
|
||||
AZStd::string m_unzipExePath;
|
||||
private:
|
||||
AZ::IO::FileIOBase* m_fileIO = nullptr;
|
||||
AZ::IO::IArchive* m_archive = nullptr;
|
||||
AZStd::vector<AZStd::thread> m_threads;
|
||||
|
||||
// Struct for tracking background threads/tasks
|
||||
struct ThreadInfo
|
||||
{
|
||||
bool shouldStop = false;
|
||||
AZStd::set<AZStd::thread::id> threads;
|
||||
};
|
||||
|
||||
AZStd::mutex m_threadControlMutex; // Guards m_threadInfoMap
|
||||
AZStd::condition_variable m_cv;
|
||||
AZStd::unordered_map<AZ::Uuid, ThreadInfo> m_threadInfoMap;
|
||||
bool CheckParamsForAdd(const AZStd::string& directory, const AZStd::string& file);
|
||||
bool CheckParamsForExtract(const AZStd::string& archive, const AZStd::string& directory);
|
||||
bool CheckParamsForCreate(const AZStd::string& archive, const AZStd::string& directory);
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -16,91 +16,62 @@ namespace AzToolsFramework
|
||||
|
||||
void NullArchiveComponent::Activate()
|
||||
{
|
||||
ArchiveCommands::Bus::Handler::BusConnect();
|
||||
ArchiveCommandsBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void NullArchiveComponent::Deactivate()
|
||||
{
|
||||
ArchiveCommands::Bus::Handler::BusDisconnect();
|
||||
ArchiveCommandsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
bool NullArchiveComponent::ExtractArchiveBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*destinationPath*/, bool /*extractWithRootDirectory*/)
|
||||
std::future<bool> DefaultFuture()
|
||||
{
|
||||
std::promise<bool> p;
|
||||
p.set_value(false);
|
||||
return p.get_future();
|
||||
}
|
||||
|
||||
std::future<bool> NullArchiveComponent::CreateArchive(
|
||||
const AZStd::string& /*archivePath*/,
|
||||
const AZStd::string& /*dirToArchive*/)
|
||||
{
|
||||
return DefaultFuture();
|
||||
}
|
||||
|
||||
std::future<bool> NullArchiveComponent::ExtractArchive(
|
||||
const AZStd::string& /*archivePath*/,
|
||||
const AZStd::string& /*destinationPath*/)
|
||||
{
|
||||
return DefaultFuture();
|
||||
}
|
||||
|
||||
std::future<bool> NullArchiveComponent::ExtractFile(
|
||||
const AZStd::string& /*archivePath*/,
|
||||
const AZStd::string& /*fileInArchive*/,
|
||||
const AZStd::string& /*destinationPath*/)
|
||||
{
|
||||
return DefaultFuture();
|
||||
}
|
||||
|
||||
bool NullArchiveComponent::ListFilesInArchive(const AZStd::string& /*archivePath*/, AZStd::vector<AZStd::string>& /*outFileEntries*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void NullArchiveComponent::ExtractArchive(const AZStd::string& /*archivePath*/, const AZStd::string& /*destinationPath*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseCallback& respCallback)
|
||||
std::future<bool> NullArchiveComponent::AddFileToArchive(
|
||||
const AZStd::string& /*archivePath*/,
|
||||
const AZStd::string& /*fileToAdd*/,
|
||||
const AZStd::string& /*pathInArchive*/)
|
||||
{
|
||||
AZ::TickBus::QueueFunction(respCallback, false);
|
||||
return DefaultFuture();
|
||||
}
|
||||
|
||||
void NullArchiveComponent::ExtractArchiveOutput(const AZStd::string& /*archivePath*/, const AZStd::string& /*destinationPath*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
|
||||
}
|
||||
|
||||
void NullArchiveComponent::ExtractArchiveWithoutRoot(const AZStd::string& /*archivePath*/, const AZStd::string& /*destinationPath*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
|
||||
}
|
||||
|
||||
void NullArchiveComponent::ExtractFile(const AZStd::string& /*archivePath*/, const AZStd::string& /*fileInArchive*/, const AZStd::string& /*destinationPath*/, bool /*overWrite*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
// Always report we failed to extract
|
||||
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
|
||||
}
|
||||
|
||||
bool NullArchiveComponent::ExtractFileBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*fileInArchive*/, const AZStd::string& /*destinationPath*/, bool /*overWrite*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void NullArchiveComponent::ListFilesInArchive(const AZStd::string& /*archivePath*/, AZStd::vector<AZStd::string>& /*consoleOutput*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
// Always report we failed to extract
|
||||
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
|
||||
}
|
||||
|
||||
bool NullArchiveComponent::ListFilesInArchiveBlocking(const AZStd::string& /*archivePath*/, AZStd::vector<AZStd::string>& /*consoleOutput*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void NullArchiveComponent::AddFileToArchive(const AZStd::string& /*archivePath*/, const AZStd::string& /*fileToAdd*/, const AZStd::string& /*pathInArchive*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
// Always report we failed to extract
|
||||
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
|
||||
}
|
||||
|
||||
bool NullArchiveComponent::AddFileToArchiveBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*fileToAdd*/, const AZStd::string& /*pathInArchive*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NullArchiveComponent::AddFilesToArchiveBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*workingDirectory*/, const AZStd::string& /*listFilePath*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void NullArchiveComponent::AddFilesToArchive(const AZStd::string& /*archivePath*/, const AZStd::string& /*workingDirectory*/, const AZStd::string& /*listFilePath*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
// Always report we failed to extract
|
||||
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
|
||||
}
|
||||
|
||||
void NullArchiveComponent::CreateArchive(const AZStd::string& /*archivePath*/, const AZStd::string& /*dirToArchive*/, AZ::Uuid /*taskHandle*/, const ArchiveResponseOutputCallback& respCallback)
|
||||
{
|
||||
// Always report we failed to extract
|
||||
AZ::TickBus::QueueFunction(respCallback, false, AZStd::string());
|
||||
}
|
||||
|
||||
bool NullArchiveComponent::CreateArchiveBlocking(const AZStd::string& /*archivePath*/, const AZStd::string& /*dirToArchive*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void NullArchiveComponent::CancelTasks(AZ::Uuid /*taskHandle*/)
|
||||
std::future<bool> NullArchiveComponent::AddFilesToArchive(
|
||||
const AZStd::string& /*archivePath*/,
|
||||
const AZStd::string& /*workingDirectory*/,
|
||||
const AZStd::string& /*listFilePath*/)
|
||||
{
|
||||
return DefaultFuture();
|
||||
}
|
||||
|
||||
void NullArchiveComponent::Reflect(AZ::ReflectContext* context)
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
class NullArchiveComponent
|
||||
: public AZ::Component
|
||||
, private ArchiveCommands::Bus::Handler
|
||||
, private ArchiveCommandsBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(NullArchiveComponent, "{D665B6B1-5FF4-4203-B19F-BBDB82587129}")
|
||||
@@ -32,23 +32,31 @@ namespace AzToolsFramework
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ArchiveCommands::Bus::Handler overrides
|
||||
// ArchiveCommands::Bus::Handler overrides
|
||||
void CreateArchive(const AZStd::string& archivePath, const AZStd::string& dirToArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
bool CreateArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& dirToArchive) override;
|
||||
bool ExtractArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool extractWithRootDirectory) override;
|
||||
void ExtractArchive(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseCallback& respCallback) override;
|
||||
void ExtractArchiveOutput(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
void ExtractArchiveWithoutRoot(const AZStd::string& archivePath, const AZStd::string& destinationPath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
void ExtractFile(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
bool ExtractFileBlocking(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite) override;
|
||||
void ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& consoleOutput, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
bool ListFilesInArchiveBlocking(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& consoleOutput) override;
|
||||
void AddFileToArchive(const AZStd::string& archivePath, const AZStd::string& fileToAdd, const AZStd::string& pathInArchive, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
bool AddFileToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& fileToAdd, const AZStd::string& pathInArchive) override;
|
||||
bool AddFilesToArchiveBlocking(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath) override;
|
||||
void AddFilesToArchive(const AZStd::string& archivePath, const AZStd::string& workingDirectory, const AZStd::string& listFilePath, AZ::Uuid taskHandle, const ArchiveResponseOutputCallback& respCallback) override;
|
||||
void CancelTasks(AZ::Uuid taskHandle) override;
|
||||
// ArchiveCommandsBus::Handler overrides
|
||||
[[nodiscard]] std::future<bool> CreateArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& dirToArchive) override;
|
||||
|
||||
[[nodiscard]] std::future<bool> ExtractArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& destinationPath) override;
|
||||
|
||||
[[nodiscard]] std::future<bool> ExtractFile(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& fileInArchive,
|
||||
const AZStd::string& destinationPath) override;
|
||||
|
||||
bool ListFilesInArchive(const AZStd::string& archivePath, AZStd::vector<AZStd::string>& outFileEntries) override;
|
||||
|
||||
[[nodiscard]] std::future<bool> AddFileToArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& workingDirectory,
|
||||
const AZStd::string& fileToAdd) override;
|
||||
|
||||
[[nodiscard]] std::future<bool> AddFilesToArchive(
|
||||
const AZStd::string& archivePath,
|
||||
const AZStd::string& workingDirectory,
|
||||
const AZStd::string& listFilePath) override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
#include <AzToolsFramework/Asset/AssetProcessorMessages.h>
|
||||
#include <AzToolsFramework/Asset/AssetSystemComponent.h>
|
||||
#include <AzToolsFramework/AssetEditor/AssetEditorBus.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ AZ_PUSH_DISABLE_WARNING(4251 4244, "-Wunknown-warning-option") // disable warnin
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
AZ_CVAR(
|
||||
bool, ed_hideAssetPickerPathColumn, false, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
bool, ed_hideAssetPickerPathColumn, true, nullptr, AZ::ConsoleFunctorFlags::Null,
|
||||
"Hide AssetPicker path column for a clearer view.");
|
||||
AZ_CVAR_EXTERNED(bool, ed_useNewAssetBrowserTableView);
|
||||
|
||||
|
||||
+1
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
|
||||
|
||||
|
||||
+1
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/IO/Path/Path.h>
|
||||
|
||||
+1
-1
@@ -290,7 +290,7 @@ namespace AzToolsFramework
|
||||
absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild;
|
||||
break;
|
||||
}
|
||||
bool pixmapLoadedSuccess = pixmap.load(absoluteIconPath.c_str());
|
||||
[[maybe_unused]] bool pixmapLoadedSuccess = pixmap.load(absoluteIconPath.c_str());
|
||||
AZ_Assert(pixmapLoadedSuccess, "Error loading Branch Icons in SearchEntryDelegate");
|
||||
|
||||
m_branchIcons[static_cast<EntryBranchType>(branchType)] = pixmap;
|
||||
|
||||
+18
-10
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Debug/Trace.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
@@ -31,12 +32,10 @@ namespace AzToolsFramework
|
||||
const int NumOfBytesInMB = 1024 * 1024;
|
||||
const int ManifestFileSizeBufferInBytes = 10 * 1024; // 10 KB
|
||||
const float AssetCatalogFileSizeBufferPercentage = 1.0f;
|
||||
using ArchiveCommandsBus = AzToolsFramework::ArchiveCommands::Bus;
|
||||
using AssetCatalogRequestBus = AZ::Data::AssetCatalogRequestBus;
|
||||
|
||||
const char AssetBundleComponent::DeltaCatalogName[] = "DeltaCatalog.xml";
|
||||
|
||||
constexpr int SleepTimeMS = 250;
|
||||
constexpr int InjectFileRetryCount = 4;
|
||||
|
||||
|
||||
@@ -135,7 +134,7 @@ namespace AzToolsFramework
|
||||
AZ_TracePrintf(logWindowName, "Gathering file entries in source pak file \"%s\".\n", sourcePak.c_str());
|
||||
bool result = false;
|
||||
AZStd::vector<AZStd::string> fileEntries;
|
||||
ArchiveCommandsBus::BroadcastResult(result, &AzToolsFramework::ArchiveCommands::ListFilesInArchiveBlocking, normalizedSourcePakPath, fileEntries);
|
||||
ArchiveCommandsBus::BroadcastResult(result, &AzToolsFramework::ArchiveCommandsBus::Events::ListFilesInArchive, normalizedSourcePakPath, fileEntries);
|
||||
// This ebus currently always returns false as the result, as it is believed that the 7z process is
|
||||
// being terminated by the user instead of ending gracefully. Check against an empty fileList instead
|
||||
// as a result.
|
||||
@@ -605,15 +604,17 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_TracePrintf(logWindowName, "Injecting file (%s) into bundle (%s).\n", filePath.c_str(), archiveFilePath.c_str());
|
||||
bool fileAddedToArchive = false;
|
||||
std::future<bool> fileAdded;
|
||||
int retryCount = InjectFileRetryCount;
|
||||
|
||||
while (!fileAddedToArchive && retryCount)
|
||||
{
|
||||
ArchiveCommandsBus::BroadcastResult(fileAddedToArchive, &AzToolsFramework::ArchiveCommands::AddFileToArchiveBlocking, archiveFilePath, workingDirectory, filePath);
|
||||
ArchiveCommandsBus::BroadcastResult(fileAdded, &AzToolsFramework::ArchiveCommandsBus::Events::AddFileToArchive, archiveFilePath, workingDirectory, filePath);
|
||||
--retryCount;
|
||||
fileAddedToArchive = fileAdded.get();
|
||||
if (!fileAddedToArchive && retryCount)
|
||||
{
|
||||
AZ_Error(logWindowName, false, "Failed to insert file (%s) into bundle (%s). Retrying.", filePath.c_str(), archiveFilePath.c_str());
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(SleepTimeMS));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,7 +627,11 @@ namespace AzToolsFramework
|
||||
|
||||
bool AssetBundleComponent::InjectFile(const AZStd::string& filePath, const AZStd::string& sourcePak)
|
||||
{
|
||||
return InjectFile(filePath, sourcePak, "");
|
||||
// When no working directory is specified, assume that the file being injected goes into the root of the archive.
|
||||
// The filePath should be an absolute path, making the workingDirectory be the path leading up to the file.
|
||||
AZ::IO::PathView fullFilePath{ filePath, AZ::IO::PosixPathSeparator };
|
||||
AZ::IO::Path workingDir{ fullFilePath.ParentPath() };
|
||||
return InjectFile(filePath, sourcePak, workingDir.c_str());
|
||||
}
|
||||
|
||||
bool AssetBundleComponent::InjectFiles(const AZStd::vector<AZStd::string>& fileEntries, const AZStd::string& sourcePak, const char* workingDirectory)
|
||||
@@ -667,8 +672,9 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool filesAddedToArchive = false;
|
||||
AzToolsFramework::ArchiveCommandsBus::BroadcastResult(filesAddedToArchive, &AzToolsFramework::ArchiveCommands::AddFilesToArchiveBlocking, sourcePak, workingDirectory, listFilePath);
|
||||
std::future<bool> filesAdded;
|
||||
AzToolsFramework::ArchiveCommandsBus::BroadcastResult(filesAdded, &AzToolsFramework::ArchiveCommands::AddFilesToArchive, sourcePak, workingDirectory, listFilePath);
|
||||
bool filesAddedToArchive = filesAdded.get();
|
||||
if (!filesAddedToArchive)
|
||||
{
|
||||
AZ_Error(logWindowName, false, "Failed to insert files into bundle (%s).\n", sourcePak.c_str());
|
||||
@@ -687,7 +693,6 @@ namespace AzToolsFramework
|
||||
{
|
||||
// open the manifest and deserialize it
|
||||
bool manifestExtracted = false;
|
||||
const bool overwriteExisting = true;
|
||||
|
||||
TemporaryDir tempDir(sourcePak);
|
||||
if (!tempDir.m_result)
|
||||
@@ -697,7 +702,10 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::string manifestFilePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(tempDir.m_tempFolderPath.c_str(), AzFramework::AssetBundleManifest::s_manifestFileName, manifestFilePath, true);
|
||||
ArchiveCommandsBus::BroadcastResult(manifestExtracted, &ArchiveCommandsBus::Events::ExtractFileBlocking, sourcePak, AzFramework::AssetBundleManifest::s_manifestFileName, tempDir.m_tempFolderPath, overwriteExisting);
|
||||
|
||||
std::future<bool> extractResult;
|
||||
ArchiveCommandsBus::BroadcastResult(extractResult, &ArchiveCommandsBus::Events::ExtractFile, sourcePak, AzFramework::AssetBundleManifest::s_manifestFileName, tempDir.m_tempFolderPath);
|
||||
manifestExtracted = extractResult.get();
|
||||
if (!manifestExtracted)
|
||||
{
|
||||
AZ_Error(logWindowName, false, "Failed to extract existing manifest from archive \"%s\".", sourcePak.c_str());
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace AzToolsFramework
|
||||
//! Returns true if the file at filePath was successfully injected into the bundle at sourcePak
|
||||
static bool InjectFile(const AZStd::string& filePath, const AZStd::string& sourcePak, const char* workingDirectory);
|
||||
|
||||
//! Inject the files with relative filePaths which espect to the working directory into the bundle at sourcePak
|
||||
//! Inject the files with relative filePaths with respect to the working directory into the bundle at sourcePak
|
||||
//! Returns true if the file at filePath was successfully injected into the bundle at sourcePak
|
||||
static bool InjectFiles(const AZStd::vector<AZStd::string>& fileEntries, const AZStd::string& sourcePak, const char* workingDirectory);
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "AssetEditorBus.h"
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework::AssetEditor
|
||||
{
|
||||
void AssetEditorWindowSettings::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<AssetEditorWindowSettings>()
|
||||
->Field("m_openAssets", &AssetEditorWindowSettings::m_openAssets)
|
||||
;
|
||||
}
|
||||
}
|
||||
} // namespace AzToolsFramework::AssetEditor
|
||||
@@ -8,12 +8,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzCore/UserSettings/UserSettings.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
|
||||
namespace AZ { namespace Data { class AssetData; } }
|
||||
namespace AZ::Data
|
||||
{
|
||||
class AssetData;
|
||||
}
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
@@ -45,15 +47,7 @@ namespace AzToolsFramework
|
||||
|
||||
static constexpr const char* s_name = "AssetEditorWindowSettings";
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<AssetEditorWindowSettings>()
|
||||
->Field("m_openAssets", &AssetEditorWindowSettings::m_openAssets)
|
||||
;
|
||||
}
|
||||
}
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
};
|
||||
|
||||
// External interaction with Asset Editor
|
||||
|
||||
@@ -22,6 +22,7 @@ AZ_POP_DISABLE_WARNING
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Asset/AssetTypeInfoBus.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
|
||||
+17
-2
@@ -8,8 +8,9 @@
|
||||
|
||||
#include "ComponentModeCollection.h"
|
||||
|
||||
#include <AzToolsFramework/Commands/ComponentModeCommand.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/Commands/ComponentModeCommand.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(ComponentModeCollection, AZ::SystemAllocator, 0)
|
||||
|
||||
static const char* const s_nextActiveComponentModeTitle = "Edit Next";
|
||||
static const char* const s_nextActiveComponentModeTitle = "Edit Next";
|
||||
static const char* const s_previousActiveComponentModeTitle = "Edit Previous";
|
||||
static const char* const s_nextActiveComponentModeDesc = "Move to the next component";
|
||||
static const char* const s_prevActiveComponentModeDesc = "Move to the previous component";
|
||||
@@ -119,6 +120,11 @@ namespace AzToolsFramework
|
||||
}
|
||||
};
|
||||
|
||||
ComponentModeCollection::ComponentModeCollection(ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
|
||||
: m_viewportEditorModeTracker(viewportEditorModeTracker)
|
||||
{
|
||||
}
|
||||
|
||||
void ComponentModeCollection::AddComponentMode(
|
||||
const AZ::EntityComponentIdPair& entityComponentIdPair, const AZ::Uuid componentType,
|
||||
const ComponentModeFactoryFunction& componentModeBuilder)
|
||||
@@ -209,6 +215,11 @@ namespace AzToolsFramework
|
||||
GetEntityContextId(), &EditorComponentModeNotifications::EnteredComponentMode,
|
||||
m_activeComponentTypes);
|
||||
|
||||
// this call to activate the component mode editor state should eventually replace the bus call in
|
||||
// ComponentModeCollection::BeginComponentMode() to EditorComponentModeNotifications::EnteredComponentMode
|
||||
// such that all of the notifications for activating/deactivating the different editor modes are in a central location
|
||||
m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Component);
|
||||
|
||||
// enable actions for the first/primary ComponentMode
|
||||
// note: if multiple ComponentModes are activated at the same time, actions
|
||||
// are not available together, the 'active' mode will bind its actions one at a time
|
||||
@@ -282,6 +293,10 @@ namespace AzToolsFramework
|
||||
&EditorComponentModeNotifications::LeftComponentMode,
|
||||
m_activeComponentTypes);
|
||||
|
||||
// this call to deactivate the component mode editor state should eventually replace the bus call in
|
||||
// ComponentModeCollection::EndComponentMode() to EditorComponentModeNotifications::LeftComponentMode
|
||||
// such that all of the notifications for activating/deactivating the different editor modes are in a central location
|
||||
m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Component);
|
||||
|
||||
// clear stored modes and builders for this ComponentMode
|
||||
// TLDR: avoid 'use after free' error
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EditorMetricsEventsBusTraits;
|
||||
class ViewportEditorModeTrackerInterface;
|
||||
|
||||
namespace ComponentModeFramework
|
||||
{
|
||||
@@ -25,7 +26,7 @@ namespace AzToolsFramework
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
|
||||
/// @cond
|
||||
ComponentModeCollection() = default;
|
||||
explicit ComponentModeCollection(ViewportEditorModeTrackerInterface* viewportEditorModeTracker);
|
||||
~ComponentModeCollection() = default;
|
||||
ComponentModeCollection(const ComponentModeCollection&) = delete;
|
||||
ComponentModeCollection& operator=(const ComponentModeCollection&) = delete;
|
||||
@@ -101,6 +102,7 @@ namespace AzToolsFramework
|
||||
size_t m_selectedComponentModeIndex = 0; ///< Index into the array of active ComponentModes, current index is 'selected' ComponentMode.
|
||||
bool m_adding = false; ///< Are we currently adding individual ComponentModes to the Editor wide ComponentMode.
|
||||
bool m_componentMode = false; ///< Editor (global) ComponentMode flag - is ComponentMode active or not.
|
||||
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes.
|
||||
};
|
||||
} // namespace ComponentModeFramework
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzCore/std/algorithm.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
#include <AzCore/RTTI/AttributeReader.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzToolsFramework/Commands/EntityStateCommand.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
+21
-17
@@ -18,8 +18,9 @@
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h>
|
||||
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabLoader.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
@@ -317,17 +318,18 @@ namespace AzToolsFramework
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Prefab::Instance>>&& nestedPrefabInstances,
|
||||
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder)
|
||||
{
|
||||
AZStd::unique_ptr<Prefab::Instance> createdPrefabInstance =
|
||||
m_prefabSystemComponent->CreatePrefab(entities, AZStd::move(nestedPrefabInstances), filePath, nullptr, false);
|
||||
if (!instanceToParentUnder)
|
||||
{
|
||||
instanceToParentUnder = *m_rootInstance;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Prefab::Instance> createdPrefabInstance = m_prefabSystemComponent->CreatePrefab(
|
||||
entities, AZStd::move(nestedPrefabInstances), filePath, nullptr, instanceToParentUnder, false);
|
||||
|
||||
if (createdPrefabInstance)
|
||||
{
|
||||
if (!instanceToParentUnder)
|
||||
{
|
||||
instanceToParentUnder = *m_rootInstance;
|
||||
}
|
||||
|
||||
Prefab::Instance& addedInstance = instanceToParentUnder->get().AddInstance(AZStd::move(createdPrefabInstance));
|
||||
Prefab::Instance& addedInstance = instanceToParentUnder->get().AddInstance(
|
||||
AZStd::move(createdPrefabInstance));
|
||||
AZ::Entity* containerEntity = addedInstance.m_containerEntity.get();
|
||||
containerEntity->AddComponent(aznew Prefab::EditorPrefabComponent());
|
||||
HandleEntitiesAdded({containerEntity});
|
||||
@@ -341,16 +343,18 @@ namespace AzToolsFramework
|
||||
Prefab::InstanceOptionalReference PrefabEditorEntityOwnershipService::InstantiatePrefab(
|
||||
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder)
|
||||
{
|
||||
AZStd::unique_ptr<Prefab::Instance> createdPrefabInstance = m_prefabSystemComponent->InstantiatePrefab(filePath);
|
||||
|
||||
if (createdPrefabInstance)
|
||||
if (!instanceToParentUnder)
|
||||
{
|
||||
if (!instanceToParentUnder)
|
||||
{
|
||||
instanceToParentUnder = *m_rootInstance;
|
||||
}
|
||||
instanceToParentUnder = *m_rootInstance;
|
||||
}
|
||||
|
||||
Prefab::Instance& addedInstance = instanceToParentUnder->get().AddInstance(AZStd::move(createdPrefabInstance));
|
||||
AZStd::unique_ptr<Prefab::Instance> instantiatedPrefabInstance =
|
||||
m_prefabSystemComponent->InstantiatePrefab(filePath, instanceToParentUnder);
|
||||
|
||||
if (instantiatedPrefabInstance)
|
||||
{
|
||||
Prefab::Instance& addedInstance = instanceToParentUnder->get().AddInstance(
|
||||
AZStd::move(instantiatedPrefabInstance));
|
||||
HandleEntitiesAdded({addedInstance.m_containerEntity.get()});
|
||||
return addedInstance;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! FocusModeInterface
|
||||
@@ -26,11 +29,11 @@ namespace AzToolsFramework
|
||||
virtual void SetFocusRoot(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Clears the Editor focus, allowing the user to select the whole level again.
|
||||
virtual void ClearFocusRoot() = 0;
|
||||
virtual void ClearFocusRoot(AzFramework::EntityContextId entityContextId) = 0;
|
||||
|
||||
//! Returns the entity id of the root of the current Editor focus.
|
||||
//! @return The entity id of the root of the Editor focus, or an invalid entity id if no focus is set.
|
||||
virtual AZ::EntityId GetFocusRoot() = 0;
|
||||
virtual AZ::EntityId GetFocusRoot(AzFramework::EntityContextId entityContextId) = 0;
|
||||
|
||||
//! Returns whether the entity id provided is part of the focused sub-tree.
|
||||
virtual bool IsInFocusSubTree(AZ::EntityId entityId) const = 0;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
//! Used to notify when the editor focus changes.
|
||||
class FocusModeNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = AzFramework::EntityContextId;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Triggered when the editor focus is changed to a different entity.
|
||||
//! @param entityId The entity the focus has been moved to.
|
||||
virtual void OnEditorFocusChanged(AZ::EntityId entityId) = 0;
|
||||
|
||||
protected:
|
||||
~FocusModeNotifications() = default;
|
||||
};
|
||||
|
||||
using FocusModeNotificationBus = AZ::EBus<FocusModeNotifications>;
|
||||
|
||||
} // namespace AzToolsFramework
|
||||
+23
-4
@@ -9,7 +9,9 @@
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeNotificationBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -64,17 +66,34 @@ namespace AzToolsFramework
|
||||
|
||||
void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId)
|
||||
{
|
||||
m_focusRoot = entityId;
|
||||
if (m_focusRoot == entityId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode
|
||||
m_focusRoot = entityId;
|
||||
FocusModeNotificationBus::Broadcast(&FocusModeNotifications::OnEditorFocusChanged, m_focusRoot);
|
||||
|
||||
if (auto tracker = AZ::Interface<ViewportEditorModeTrackerInterface>::Get();
|
||||
tracker != nullptr)
|
||||
{
|
||||
if (!m_focusRoot.IsValid() && entityId.IsValid())
|
||||
{
|
||||
tracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Focus);
|
||||
}
|
||||
else if (m_focusRoot.IsValid() && !entityId.IsValid())
|
||||
{
|
||||
tracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Focus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FocusModeSystemComponent::ClearFocusRoot()
|
||||
void FocusModeSystemComponent::ClearFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId)
|
||||
{
|
||||
SetFocusRoot(AZ::EntityId());
|
||||
}
|
||||
|
||||
AZ::EntityId FocusModeSystemComponent::GetFocusRoot()
|
||||
AZ::EntityId FocusModeSystemComponent::GetFocusRoot([[maybe_unused]] AzFramework::EntityContextId entityContextId)
|
||||
{
|
||||
return m_focusRoot;
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,8 +40,8 @@ namespace AzToolsFramework
|
||||
|
||||
// FocusModeInterface overrides ...
|
||||
void SetFocusRoot(AZ::EntityId entityId) override;
|
||||
void ClearFocusRoot() override;
|
||||
AZ::EntityId GetFocusRoot() override;
|
||||
void ClearFocusRoot(AzFramework::EntityContextId entityContextId) override;
|
||||
AZ::EntityId GetFocusRoot(AzFramework::EntityContextId entityContextId) override;
|
||||
bool IsInFocusSubTree(AZ::EntityId entityId) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <AzCore/Math/Quaternion.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/smart_ptr/enable_shared_from_this.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorBus.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorSpace.h>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/TemplateInstanceMapperInterface.h>
|
||||
|
||||
@@ -28,24 +29,52 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
Instance::Instance(AZStd::unique_ptr<AZ::Entity> containerEntity)
|
||||
: Instance(AZStd::move(containerEntity), AZStd::nullopt, GenerateInstanceAlias())
|
||||
{
|
||||
m_instanceEntityMapper = AZ::Interface<InstanceEntityMapperInterface>::Get();
|
||||
}
|
||||
|
||||
Instance::Instance(InstanceOptionalReference parent)
|
||||
: Instance(nullptr, parent, GenerateInstanceAlias())
|
||||
{
|
||||
}
|
||||
|
||||
Instance::Instance(InstanceAlias alias)
|
||||
: Instance(nullptr, AZStd::nullopt, AZStd::move(alias))
|
||||
{
|
||||
}
|
||||
|
||||
Instance::Instance(AZStd::unique_ptr<AZ::Entity> containerEntity, InstanceOptionalReference parent)
|
||||
: Instance(AZStd::move(containerEntity), parent, GenerateInstanceAlias())
|
||||
{
|
||||
}
|
||||
|
||||
Instance::Instance(AZStd::unique_ptr<AZ::Entity> containerEntity, InstanceOptionalReference parent, InstanceAlias alias)
|
||||
: m_parent(parent.has_value() ? &parent->get() : nullptr)
|
||||
, m_alias(AZStd::move(alias))
|
||||
, m_containerEntity(containerEntity ? AZStd::move(containerEntity) : AZStd::make_unique<AZ::Entity>())
|
||||
, m_instanceEntityMapper(AZ::Interface<InstanceEntityMapperInterface>::Get())
|
||||
, m_templateInstanceMapper(AZ::Interface<TemplateInstanceMapperInterface>::Get())
|
||||
{
|
||||
AZ_Assert(m_instanceEntityMapper,
|
||||
"Instance Entity Mapper Interface could not be found. "
|
||||
"It is a requirement for the Prefab Instance class. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
m_templateInstanceMapper = AZ::Interface<TemplateInstanceMapperInterface>::Get();
|
||||
|
||||
AZ_Assert(m_templateInstanceMapper,
|
||||
"Template Instance Mapper Interface could not be found. "
|
||||
"It is a requirement for the Prefab Instance class. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
m_alias = GenerateInstanceAlias();
|
||||
m_containerEntity = containerEntity ? AZStd::move(containerEntity)
|
||||
: AZStd::make_unique<AZ::Entity>();
|
||||
if (parent)
|
||||
{
|
||||
AliasPath absoluteInstancePath = m_parent->GetAbsoluteInstanceAliasPath();
|
||||
absoluteInstancePath.Append(m_alias);
|
||||
absoluteInstancePath.Append(PrefabDomUtils::ContainerEntityName);
|
||||
|
||||
AZ::EntityId newContainerEntityId = InstanceEntityIdMapper::GenerateEntityIdForAliasPath(absoluteInstancePath);
|
||||
m_containerEntity->SetId(newContainerEntityId);
|
||||
}
|
||||
|
||||
RegisterEntity(m_containerEntity->GetId(), PrefabDomUtils::ContainerEntityName);
|
||||
}
|
||||
|
||||
@@ -69,12 +98,12 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
const TemplateId& Instance::GetTemplateId() const
|
||||
TemplateId Instance::GetTemplateId() const
|
||||
{
|
||||
return m_templateId;
|
||||
}
|
||||
|
||||
void Instance::SetTemplateId(const TemplateId& templateId)
|
||||
void Instance::SetTemplateId(TemplateId templateId)
|
||||
{
|
||||
// If we aren't changing the template Id, there's no need to unregister / re-register
|
||||
if (templateId == m_templateId)
|
||||
@@ -295,20 +324,21 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance)
|
||||
{
|
||||
InstanceAlias newInstanceAlias = GenerateInstanceAlias();
|
||||
return AddInstance(AZStd::move(instance), newInstanceAlias);
|
||||
}
|
||||
|
||||
Instance& Instance::AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias newInstanceAlias)
|
||||
{
|
||||
AZ_Assert(instance.get(), "instance argument is nullptr");
|
||||
|
||||
if (instance->GetInstanceAlias().empty())
|
||||
{
|
||||
instance->m_alias = GenerateInstanceAlias();
|
||||
}
|
||||
|
||||
AZ_Assert(
|
||||
m_nestedInstances.find(newInstanceAlias) == m_nestedInstances.end(),
|
||||
m_nestedInstances.find(instance->GetInstanceAlias()) == m_nestedInstances.end(),
|
||||
"InstanceAlias' unique id collision, this should never happen.");
|
||||
|
||||
instance->m_parent = this;
|
||||
instance->m_alias = newInstanceAlias;
|
||||
return *(m_nestedInstances[newInstanceAlias] = std::move(instance));
|
||||
auto& alias = instance->GetInstanceAlias();
|
||||
return *(m_nestedInstances[alias] = AZStd::move(instance));
|
||||
}
|
||||
|
||||
void Instance::DetachNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>)>& callback)
|
||||
|
||||
@@ -65,6 +65,9 @@ namespace AzToolsFramework
|
||||
|
||||
Instance();
|
||||
explicit Instance(AZStd::unique_ptr<AZ::Entity> containerEntity);
|
||||
explicit Instance(InstanceOptionalReference parent);
|
||||
explicit Instance(AZStd::unique_ptr<AZ::Entity> containerEntity, InstanceOptionalReference parent);
|
||||
explicit Instance(InstanceAlias alias);
|
||||
virtual ~Instance();
|
||||
|
||||
Instance(const Instance& rhs) = delete;
|
||||
@@ -72,8 +75,8 @@ namespace AzToolsFramework
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
const TemplateId& GetTemplateId() const;
|
||||
void SetTemplateId(const TemplateId& templateId);
|
||||
TemplateId GetTemplateId() const;
|
||||
void SetTemplateId(TemplateId templateId);
|
||||
|
||||
const AZ::IO::Path& GetTemplateSourcePath() const;
|
||||
void SetTemplateSourcePath(AZ::IO::PathView sourcePath);
|
||||
@@ -97,7 +100,6 @@ namespace AzToolsFramework
|
||||
void Reset();
|
||||
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance);
|
||||
Instance& AddInstance(AZStd::unique_ptr<Instance> instance, InstanceAlias instanceAlias);
|
||||
AZStd::unique_ptr<Instance> DetachNestedInstance(const InstanceAlias& instanceAlias);
|
||||
void DetachNestedInstances(const AZStd::function<void(AZStd::unique_ptr<Instance>)>& callback);
|
||||
|
||||
@@ -184,6 +186,8 @@ namespace AzToolsFramework
|
||||
private:
|
||||
static constexpr const char s_aliasPathSeparator = '/';
|
||||
|
||||
Instance(AZStd::unique_ptr<AZ::Entity> containerEntity, InstanceOptionalReference parent, InstanceAlias alias);
|
||||
|
||||
void ClearEntities();
|
||||
|
||||
void RemoveEntities(const AZStd::function<bool(const AZStd::unique_ptr<AZ::Entity>&)>& filter);
|
||||
|
||||
+2
-1
@@ -46,10 +46,11 @@ namespace AzToolsFramework
|
||||
//! Updates the template links (updating instances) for the given template and triggers propagation on its instances.
|
||||
//! @param providedPatch The patch to apply to the template.
|
||||
//! @param templateId The id of the template to update.
|
||||
//! @param immediate An optional flag whether to apply the patch immediately (needed for Undo/Redos) or wait until next system tick.
|
||||
//! @param instanceToExclude An optional reference to an instance of the template being updated that should not be refreshes as part of propagation.
|
||||
//! Defaults to nullopt, which means that all instances will be refreshed.
|
||||
//! @return True if the template was patched correctly, false if the operation failed.
|
||||
virtual bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;
|
||||
virtual bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;
|
||||
|
||||
virtual void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) = 0;
|
||||
|
||||
|
||||
+2
-2
@@ -156,7 +156,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude)
|
||||
bool InstanceToTemplatePropagator::PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude)
|
||||
{
|
||||
PrefabDom& templateDomReference = m_prefabSystemComponentInterface->FindTemplateDom(templateId);
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace AzToolsFramework
|
||||
(result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::PartialSkip),
|
||||
"Some of the patches were not successfully applied.");
|
||||
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, true);
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId, instanceToExclude);
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId, immediate, instanceToExclude);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ namespace AzToolsFramework
|
||||
|
||||
InstanceOptionalReference GetTopMostInstanceInHierarchy(AZ::EntityId entityId) override;
|
||||
|
||||
bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override;
|
||||
bool PatchTemplate(PrefabDomValue& providedPatch, TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override;
|
||||
|
||||
void ApplyPatchesToInstance(const AZ::EntityId& entityId, PrefabDom& patches, const Instance& instanceToAddPatches) override;
|
||||
|
||||
|
||||
+6
-1
@@ -52,7 +52,7 @@ namespace AzToolsFramework
|
||||
AZ::Interface<InstanceUpdateExecutorInterface>::Unregister(this);
|
||||
}
|
||||
|
||||
void InstanceUpdateExecutor::AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude)
|
||||
void InstanceUpdateExecutor::AddTemplateInstancesToQueue(TemplateId instanceTemplateId, bool immediate, InstanceOptionalReference instanceToExclude)
|
||||
{
|
||||
auto findInstancesResult =
|
||||
m_templateInstanceMapperInterface->FindInstancesOwnedByTemplate(instanceTemplateId);
|
||||
@@ -79,6 +79,11 @@ namespace AzToolsFramework
|
||||
m_instancesUpdateQueue.emplace_back(instance);
|
||||
}
|
||||
}
|
||||
|
||||
if (immediate)
|
||||
{
|
||||
UpdateTemplateInstancesInQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceUpdateExecutor::RemoveTemplateInstanceFromQueue(const Instance* instance)
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ namespace AzToolsFramework
|
||||
|
||||
explicit InstanceUpdateExecutor(int instanceCountToUpdateInBatch = 0);
|
||||
|
||||
void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override;
|
||||
void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override;
|
||||
bool UpdateTemplateInstancesInQueue() override;
|
||||
virtual void RemoveTemplateInstanceFromQueue(const Instance* instance) override;
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ namespace AzToolsFramework
|
||||
virtual ~InstanceUpdateExecutorInterface() = default;
|
||||
|
||||
// Add all Instances of Template with given Id into a queue for updating them later.
|
||||
virtual void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;
|
||||
virtual void AddTemplateInstancesToQueue(TemplateId instanceTemplateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;
|
||||
|
||||
// Update Instances in the waiting queue.
|
||||
virtual bool UpdateTemplateInstancesInQueue() = 0;
|
||||
|
||||
+4
-4
@@ -27,7 +27,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
|
||||
bool TemplateInstanceMapper::RegisterTemplate(const TemplateId& templateId)
|
||||
bool TemplateInstanceMapper::RegisterTemplate(TemplateId templateId)
|
||||
{
|
||||
const bool result = m_templateIdToInstancesMap.emplace(templateId, InstanceSet()).second;
|
||||
AZ_Assert(result,
|
||||
@@ -39,7 +39,7 @@ namespace AzToolsFramework
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TemplateInstanceMapper::UnregisterTemplate(const TemplateId& templateId)
|
||||
bool TemplateInstanceMapper::UnregisterTemplate(TemplateId templateId)
|
||||
{
|
||||
const bool result = m_templateIdToInstancesMap.erase(templateId) != 0;
|
||||
AZ_Assert(result,
|
||||
@@ -53,7 +53,7 @@ namespace AzToolsFramework
|
||||
|
||||
bool TemplateInstanceMapper::RegisterInstanceToTemplate(Instance& instance)
|
||||
{
|
||||
const TemplateId& templateId = instance.GetTemplateId();
|
||||
TemplateId templateId = instance.GetTemplateId();
|
||||
if (templateId == InvalidTemplateId)
|
||||
{
|
||||
return false;
|
||||
@@ -79,7 +79,7 @@ namespace AzToolsFramework
|
||||
found->second.erase(&instance) != 0;
|
||||
}
|
||||
|
||||
InstanceSetConstReference TemplateInstanceMapper::FindInstancesOwnedByTemplate(const TemplateId& templateId) const
|
||||
InstanceSetConstReference TemplateInstanceMapper::FindInstancesOwnedByTemplate(TemplateId templateId) const
|
||||
{
|
||||
auto found = m_templateIdToInstancesMap.find(templateId);
|
||||
|
||||
|
||||
+3
-3
@@ -26,10 +26,10 @@ namespace AzToolsFramework
|
||||
TemplateInstanceMapper();
|
||||
~TemplateInstanceMapper() override;
|
||||
|
||||
InstanceSetConstReference FindInstancesOwnedByTemplate(const TemplateId& templateId) const override;
|
||||
InstanceSetConstReference FindInstancesOwnedByTemplate(TemplateId templateId) const override;
|
||||
|
||||
bool RegisterTemplate(const TemplateId& templateId);
|
||||
bool UnregisterTemplate(const TemplateId& templateId);
|
||||
bool RegisterTemplate(TemplateId templateId);
|
||||
bool UnregisterTemplate(TemplateId templateId);
|
||||
|
||||
protected:
|
||||
bool RegisterInstanceToTemplate(Instance& instance) override;
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ namespace AzToolsFramework
|
||||
AZ_RTTI(TemplateInstanceMapperInterface, "{5DCCCDAA-3441-4266-9670-B349386E0129}");
|
||||
|
||||
virtual ~TemplateInstanceMapperInterface() = default;
|
||||
virtual InstanceSetConstReference FindInstancesOwnedByTemplate(const TemplateId& templateId) const = 0;
|
||||
virtual InstanceSetConstReference FindInstancesOwnedByTemplate(TemplateId templateId) const = 0;
|
||||
|
||||
protected:
|
||||
// Only the Instance class is allowed to register and unregister Instances.
|
||||
|
||||
@@ -122,12 +122,12 @@ namespace AzToolsFramework
|
||||
!m_instanceName.empty();
|
||||
}
|
||||
|
||||
const TemplateId& Link::GetSourceTemplateId() const
|
||||
TemplateId Link::GetSourceTemplateId() const
|
||||
{
|
||||
return m_sourceTemplateId;
|
||||
}
|
||||
|
||||
const TemplateId& Link::GetTargetTemplateId() const
|
||||
TemplateId Link::GetTargetTemplateId() const
|
||||
{
|
||||
return m_targetTemplateId;
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ namespace AzToolsFramework
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
const TemplateId& GetSourceTemplateId() const;
|
||||
const TemplateId& GetTargetTemplateId() const;
|
||||
TemplateId GetSourceTemplateId() const;
|
||||
TemplateId GetTargetTemplateId() const;
|
||||
|
||||
LinkId GetId() const;
|
||||
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusHandler.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusNotificationBus.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
@@ -23,12 +25,14 @@ namespace AzToolsFramework::Prefab
|
||||
"Instance Entity Mapper Interface could not be found. "
|
||||
"Check that it is being correctly initialized.");
|
||||
|
||||
EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
AZ::Interface<PrefabFocusInterface>::Register(this);
|
||||
}
|
||||
|
||||
PrefabFocusHandler::~PrefabFocusHandler()
|
||||
{
|
||||
AZ::Interface<PrefabFocusInterface>::Unregister(this);
|
||||
EditorEntityContextNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
|
||||
@@ -53,35 +57,76 @@ namespace AzToolsFramework::Prefab
|
||||
focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
|
||||
}
|
||||
|
||||
if (!focusedInstance.has_value())
|
||||
return FocusOnPrefabInstance(focusedInstance);
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPathIndex([[maybe_unused]] AzFramework::EntityContextId entityContextId, int index)
|
||||
{
|
||||
if (index < 0 || index >= m_instanceFocusVector.size())
|
||||
{
|
||||
return AZ::Failure(AZStd::string(
|
||||
"Prefab Focus Handler: Couldn't find owning instance of entityId provided."));
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: Invalid index on FocusOnPathIndex."));
|
||||
}
|
||||
|
||||
m_focusedInstance = focusedInstance;
|
||||
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
|
||||
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
|
||||
|
||||
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
if (focusModeInterface)
|
||||
return FocusOnPrefabInstance(focusedInstance);
|
||||
}
|
||||
|
||||
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance)
|
||||
{
|
||||
if (!focusedInstance.has_value())
|
||||
{
|
||||
focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId());
|
||||
return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on."));
|
||||
}
|
||||
|
||||
if (!m_focusedInstance.has_value() || &m_focusedInstance->get() != &focusedInstance->get())
|
||||
{
|
||||
m_focusedInstance = focusedInstance;
|
||||
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
|
||||
|
||||
AZ::EntityId containerEntityId;
|
||||
|
||||
if (focusedInstance->get().GetParentInstance() != AZStd::nullopt)
|
||||
{
|
||||
containerEntityId = focusedInstance->get().GetContainerEntityId();
|
||||
|
||||
// Select the container entity
|
||||
AzToolsFramework::SelectEntity(containerEntityId);
|
||||
}
|
||||
else
|
||||
{
|
||||
containerEntityId = AZ::EntityId();
|
||||
|
||||
// Clear the selection
|
||||
AzToolsFramework::SelectEntities({});
|
||||
|
||||
}
|
||||
|
||||
// Focus on the descendants of the container entity
|
||||
if (FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get())
|
||||
{
|
||||
focusModeInterface->SetFocusRoot(containerEntityId);
|
||||
}
|
||||
|
||||
RefreshInstanceFocusList();
|
||||
PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged);
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId()
|
||||
TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
return m_focusedTemplateId;
|
||||
}
|
||||
|
||||
InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance()
|
||||
InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance(
|
||||
[[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
return m_focusedInstance;
|
||||
}
|
||||
|
||||
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId)
|
||||
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const
|
||||
{
|
||||
if (!m_focusedInstance.has_value())
|
||||
{
|
||||
@@ -99,4 +144,44 @@ namespace AzToolsFramework::Prefab
|
||||
return instance.has_value() && (&instance->get() == &m_focusedInstance->get());
|
||||
}
|
||||
|
||||
const AZ::IO::Path& PrefabFocusHandler::GetPrefabFocusPath([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
return m_instanceFocusPath;
|
||||
}
|
||||
|
||||
const int PrefabFocusHandler::GetPrefabFocusPathLength([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
|
||||
{
|
||||
return aznumeric_cast<int>(m_instanceFocusVector.size());
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::OnEntityStreamLoadSuccess()
|
||||
{
|
||||
// Focus on the root prefab (AZ::EntityId() will default to it)
|
||||
FocusOnOwningPrefab(AZ::EntityId());
|
||||
}
|
||||
|
||||
void PrefabFocusHandler::RefreshInstanceFocusList()
|
||||
{
|
||||
m_instanceFocusVector.clear();
|
||||
m_instanceFocusPath.clear();
|
||||
|
||||
AZStd::list<InstanceOptionalReference> instanceFocusList;
|
||||
|
||||
// Use a support list to easily push front while traversing the prefab hierarchy
|
||||
InstanceOptionalReference currentInstance = m_focusedInstance;
|
||||
while (currentInstance.has_value())
|
||||
{
|
||||
instanceFocusList.push_front(currentInstance);
|
||||
|
||||
currentInstance = currentInstance->get().GetParentInstance();
|
||||
}
|
||||
|
||||
// Populate internals using the support list
|
||||
for (auto& instance : instanceFocusList)
|
||||
{
|
||||
m_instanceFocusPath.Append(instance->get().GetContainerEntity()->get().GetName());
|
||||
m_instanceFocusVector.emplace_back(instance);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
@@ -21,6 +22,7 @@ namespace AzToolsFramework::Prefab
|
||||
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
|
||||
class PrefabFocusHandler final
|
||||
: private PrefabFocusInterface
|
||||
, private EditorEntityContextNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0);
|
||||
@@ -28,15 +30,26 @@ namespace AzToolsFramework::Prefab
|
||||
PrefabFocusHandler();
|
||||
~PrefabFocusHandler();
|
||||
|
||||
// PrefabFocusInterface override ...
|
||||
// PrefabFocusInterface overrides ...
|
||||
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
|
||||
TemplateId GetFocusedPrefabTemplateId() override;
|
||||
InstanceOptionalReference GetFocusedPrefabInstance() override;
|
||||
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override;
|
||||
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
|
||||
TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override;
|
||||
InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override;
|
||||
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const override;
|
||||
const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const override;
|
||||
const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const override;
|
||||
|
||||
// EditorEntityContextNotificationBus overrides ...
|
||||
void OnEntityStreamLoadSuccess() override;
|
||||
|
||||
private:
|
||||
PrefabFocusOperationResult FocusOnPrefabInstance(InstanceOptionalReference focusedInstance);
|
||||
void RefreshInstanceFocusList();
|
||||
|
||||
InstanceOptionalReference m_focusedInstance;
|
||||
TemplateId m_focusedTemplateId;
|
||||
AZStd::vector<InstanceOptionalReference> m_instanceFocusVector;
|
||||
AZ::IO::Path m_instanceFocusPath;
|
||||
|
||||
InstanceEntityMapperInterface* m_instanceEntityMapperInterface;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/Instance/Instance.h>
|
||||
#include <AzToolsFramework/Prefab/Template/Template.h>
|
||||
|
||||
@@ -28,16 +30,27 @@ namespace AzToolsFramework::Prefab
|
||||
//! @param entityId The entityId of the entity whose owning instance we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) = 0;
|
||||
|
||||
//! Set the focused prefab instance to the instance at position index of the current path.
|
||||
//! @param index The index of the instance in the current path that we want the prefab system to focus on.
|
||||
virtual PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) = 0;
|
||||
|
||||
//! Returns the template id of the instance the prefab system is focusing on.
|
||||
virtual TemplateId GetFocusedPrefabTemplateId() = 0;
|
||||
virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns a reference to the instance the prefab system is focusing on.
|
||||
virtual InstanceOptionalReference GetFocusedPrefabInstance() = 0;
|
||||
virtual InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns whether the entity belongs to the instance that is being focused on, or one of its descendants.
|
||||
//! @param entityId The entityId of the queried entity.
|
||||
//! @return true if the entity belongs to the focused instance or one of its descendants, false otherwise.
|
||||
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) = 0;
|
||||
virtual bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) const = 0;
|
||||
|
||||
//! Returns the path from the root instance to the currently focused instance.
|
||||
//! @return A path composed from the names of the container entities for the instance path.
|
||||
virtual const AZ::IO::Path& GetPrefabFocusPath(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
|
||||
//! Returns the size of the path to the currently focused instance.
|
||||
virtual const int GetPrefabFocusPathLength(AzFramework::EntityContextId entityContextId) const = 0;
|
||||
};
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
//! Used to notify when the editor focus changes.
|
||||
class PrefabFocusNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EBusTraits overrides
|
||||
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
|
||||
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
|
||||
using BusIdType = AzFramework::EntityContextId;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//! Triggered when the editor focus is changed to a different prefab.
|
||||
virtual void OnPrefabFocusChanged() = 0;
|
||||
|
||||
protected:
|
||||
~PrefabFocusNotifications() = default;
|
||||
};
|
||||
|
||||
using PrefabFocusNotificationBus = AZ::EBus<PrefabFocusNotifications>;
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
@@ -1041,7 +1041,7 @@ namespace AzToolsFramework
|
||||
PrefabUndoInstance* command = aznew PrefabUndoInstance("Entity/Instance duplication");
|
||||
command->SetParent(undoBatch.GetUndoBatch());
|
||||
command->Capture(instanceDomBefore, instanceDomAfter, commonOwningInstance->get().GetTemplateId());
|
||||
command->Redo();
|
||||
command->RedoBatched();
|
||||
|
||||
DuplicateNestedInstancesInInstance(commonOwningInstance->get(),
|
||||
instances, instanceDomAfter, duplicatedEntityAndInstanceIds, newInstanceAliasToOldInstanceMap);
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
|
||||
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
|
||||
@@ -92,7 +92,17 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::unique_ptr<Instance> PrefabSystemComponent::CreatePrefab(
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
|
||||
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity, bool shouldCreateLinks)
|
||||
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity, InstanceOptionalReference parent,
|
||||
bool shouldCreateLinks)
|
||||
{
|
||||
AZStd::unique_ptr<Instance> newInstance = AZStd::make_unique<Instance>(AZStd::move(containerEntity), parent);
|
||||
CreatePrefab(entities, AZStd::move(instancesToConsume), filePath, newInstance, shouldCreateLinks);
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::CreatePrefab(
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
|
||||
AZ::IO::PathView filePath, AZStd::unique_ptr<Instance>& newInstance, bool shouldCreateLinks)
|
||||
{
|
||||
AZ::IO::Path relativeFilePath = m_prefabLoader.GenerateRelativePath(filePath);
|
||||
if (GetTemplateIdFromFilePath(relativeFilePath) != InvalidTemplateId)
|
||||
@@ -101,11 +111,9 @@ namespace AzToolsFramework
|
||||
"Filepath %s has already been registered with the Prefab System Component",
|
||||
relativeFilePath.c_str());
|
||||
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Instance> newInstance = AZStd::make_unique<Instance>(AZStd::move(containerEntity));
|
||||
|
||||
for (AZ::Entity* entity : entities)
|
||||
{
|
||||
AZ_Assert(entity, "Prefab - Null entity passed in during Create Prefab");
|
||||
@@ -136,13 +144,11 @@ namespace AzToolsFramework
|
||||
{
|
||||
newInstance->SetTemplateId(newTemplateId);
|
||||
}
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude)
|
||||
void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude)
|
||||
{
|
||||
UpdatePrefabInstances(templateId, instanceToExclude);
|
||||
UpdatePrefabInstances(templateId, immediate, instanceToExclude);
|
||||
|
||||
auto templateIdToLinkIdsIterator = m_templateToLinkIdsMap.find(templateId);
|
||||
if (templateIdToLinkIdsIterator != m_templateToLinkIdsMap.end())
|
||||
@@ -171,9 +177,9 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::UpdatePrefabInstances(const TemplateId& templateId, InstanceOptionalReference instanceToExclude)
|
||||
void PrefabSystemComponent::UpdatePrefabInstances(TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude)
|
||||
{
|
||||
m_instanceUpdateExecutor.AddTemplateInstancesToQueue(templateId, instanceToExclude);
|
||||
m_instanceUpdateExecutor.AddTemplateInstancesToQueue(templateId, immediate, instanceToExclude);
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::UpdateLinkedInstances(AZStd::queue<LinkIds>& linkIdsQueue)
|
||||
@@ -256,7 +262,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Instance> PrefabSystemComponent::InstantiatePrefab(AZ::IO::PathView filePath)
|
||||
AZStd::unique_ptr<Instance> PrefabSystemComponent::InstantiatePrefab(
|
||||
AZ::IO::PathView filePath, InstanceOptionalReference parent)
|
||||
{
|
||||
// Retrieve the template id for the source prefab filepath
|
||||
Prefab::TemplateId templateId = GetTemplateIdFromFilePath(filePath);
|
||||
@@ -276,10 +283,11 @@ namespace AzToolsFramework
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return InstantiatePrefab(templateId);
|
||||
return InstantiatePrefab(templateId, parent);
|
||||
}
|
||||
|
||||
AZStd::unique_ptr<Instance> PrefabSystemComponent::InstantiatePrefab(const TemplateId& templateId)
|
||||
AZStd::unique_ptr<Instance> PrefabSystemComponent::InstantiatePrefab(
|
||||
TemplateId templateId, InstanceOptionalReference parent)
|
||||
{
|
||||
TemplateReference instantiatingTemplate = FindTemplate(templateId);
|
||||
|
||||
@@ -292,7 +300,7 @@ namespace AzToolsFramework
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto newInstance = AZStd::make_unique<Instance>();
|
||||
auto newInstance = AZStd::make_unique<Instance>(parent);
|
||||
Instance::EntityList newEntities;
|
||||
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*newInstance, newEntities, instantiatingTemplate->get().GetPrefabDom()))
|
||||
{
|
||||
@@ -354,7 +362,7 @@ namespace AzToolsFramework
|
||||
return newTemplateId;
|
||||
}
|
||||
|
||||
TemplateReference PrefabSystemComponent::FindTemplate(const TemplateId& id)
|
||||
TemplateReference PrefabSystemComponent::FindTemplate(TemplateId id)
|
||||
{
|
||||
auto found = m_templateIdMap.find(id);
|
||||
if (found != m_templateIdMap.end())
|
||||
@@ -466,7 +474,7 @@ namespace AzToolsFramework
|
||||
templateToChange.SetFilePath(filePath);
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::RemoveTemplate(const TemplateId& templateId)
|
||||
void PrefabSystemComponent::RemoveTemplate(TemplateId templateId)
|
||||
{
|
||||
auto findTemplateResult = FindTemplate(templateId);
|
||||
if (!findTemplateResult.has_value())
|
||||
@@ -553,8 +561,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
LinkId PrefabSystemComponent::AddLink(
|
||||
const TemplateId& sourceTemplateId,
|
||||
const TemplateId& targetTemplateId,
|
||||
TemplateId sourceTemplateId,
|
||||
TemplateId targetTemplateId,
|
||||
PrefabDomValue::MemberIterator& instanceIterator,
|
||||
InstanceOptionalReference instance)
|
||||
{
|
||||
@@ -571,10 +579,13 @@ namespace AzToolsFramework
|
||||
|
||||
Template& targetTemplate = targetTemplateReference->get();
|
||||
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
Template& sourceTemplate = sourceTemplateReference->get();
|
||||
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
|
||||
|
||||
const AZStd::string& targetTemplateFilePath = targetTemplate.GetFilePath().Native();
|
||||
const AZStd::string& sourceTemplateFilePath = sourceTemplate.GetFilePath().Native();
|
||||
#endif
|
||||
|
||||
LinkId newLinkId = CreateUniqueLinkId();
|
||||
Link newLink(newLinkId);
|
||||
@@ -616,8 +627,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
LinkId PrefabSystemComponent::CreateLink(
|
||||
const TemplateId& linkTargetId,
|
||||
const TemplateId& linkSourceId,
|
||||
TemplateId linkTargetId,
|
||||
TemplateId linkSourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
const PrefabDomConstReference linkPatches,
|
||||
const LinkId& linkId)
|
||||
@@ -774,7 +785,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
}
|
||||
|
||||
bool PrefabSystemComponent::IsTemplateDirty(const TemplateId& templateId)
|
||||
bool PrefabSystemComponent::IsTemplateDirty(TemplateId templateId)
|
||||
{
|
||||
auto templateRef = FindTemplate(templateId);
|
||||
|
||||
@@ -786,7 +797,7 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty)
|
||||
void PrefabSystemComponent::SetTemplateDirtyFlag(TemplateId templateId, bool dirty)
|
||||
{
|
||||
auto templateRef = FindTemplate(templateId);
|
||||
|
||||
@@ -818,7 +829,14 @@ namespace AzToolsFramework
|
||||
auto linkIterator = m_linkIdMap.find(linkId);
|
||||
if (linkIterator != m_linkIdMap.end())
|
||||
{
|
||||
return AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId());
|
||||
if (AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -896,8 +914,10 @@ namespace AzToolsFramework
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(AZ_ENABLE_TRACING)
|
||||
Template& sourceTemplate = sourceTemplateReference->get();
|
||||
Template& targetTemplate = targetTemplateReference->get();
|
||||
#endif
|
||||
|
||||
AZStd::string_view instanceName(instanceIterator->name.GetString(), instanceIterator->name.GetStringLength());
|
||||
|
||||
@@ -933,7 +953,7 @@ namespace AzToolsFramework
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrefabSystemComponent::GenerateLinksForNewTemplate(const TemplateId& newTemplateId, Instance& instance)
|
||||
bool PrefabSystemComponent::GenerateLinksForNewTemplate(TemplateId newTemplateId, Instance& instance)
|
||||
{
|
||||
TemplateReference newTemplateReference = FindTemplate(newTemplateId);
|
||||
if (!newTemplateReference.has_value())
|
||||
@@ -973,7 +993,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
const PrefabDomValue& source = instanceSourceReference->get();
|
||||
const TemplateId& nestedTemplateId = GetTemplateIdFromFilePath(source.GetString());
|
||||
TemplateId nestedTemplateId = GetTemplateIdFromFilePath(source.GetString());
|
||||
if (nestedTemplateId == InvalidTemplateId)
|
||||
{
|
||||
AZ_Error("Prefab", false,
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace AzToolsFramework
|
||||
* @param id A unique id of a Template.
|
||||
* @return Reference of Template if the Template exists.
|
||||
*/
|
||||
TemplateReference FindTemplate(const TemplateId& id) override;
|
||||
TemplateReference FindTemplate(TemplateId id) override;
|
||||
|
||||
/**
|
||||
* Find Link with given Link id from Prefab System Component.
|
||||
@@ -112,7 +112,7 @@ namespace AzToolsFramework
|
||||
* Remove the Template associated with the given id from Prefab System Component.
|
||||
* @param templateId A unique id of a Template.
|
||||
*/
|
||||
void RemoveTemplate(const TemplateId& templateId) override;
|
||||
void RemoveTemplate(TemplateId templateId) override;
|
||||
|
||||
/**
|
||||
* Remove all Templates from the Prefab System Component.
|
||||
@@ -121,17 +121,21 @@ namespace AzToolsFramework
|
||||
|
||||
/**
|
||||
* Generates a new Prefab Instance based on the Template whose source is stored in filepath.
|
||||
* @param filePath the path to the prefab source file containing the template being instantiated.
|
||||
* @param filePath The path to the prefab source file containing the template being instantiated.
|
||||
* @param parent Reference of the target instance the instantiated instance will be placed under.
|
||||
* @return A unique_ptr to the newly instantiated instance. Null if operation failed.
|
||||
*/
|
||||
AZStd::unique_ptr<Instance> InstantiatePrefab(AZ::IO::PathView filePath) override;
|
||||
AZStd::unique_ptr<Instance> InstantiatePrefab(
|
||||
AZ::IO::PathView filePath, InstanceOptionalReference parent = AZStd::nullopt) override;
|
||||
|
||||
/**
|
||||
* Generates a new Prefab Instance based on the Template referenced by templateId
|
||||
* @param templateId the id of the template being instantiated.
|
||||
* Generates a new Prefab Instance based on the Template referenced by templateId.
|
||||
* @param templateId The id of the template being instantiated.
|
||||
* @param parent Reference of the target instance the instantiated instance will be placed under.
|
||||
* @return A unique_ptr to the newly instantiated instance. Null if operation failed.
|
||||
*/
|
||||
AZStd::unique_ptr<Instance> InstantiatePrefab(const TemplateId& templateId) override;
|
||||
AZStd::unique_ptr<Instance> InstantiatePrefab(
|
||||
TemplateId templateId, InstanceOptionalReference parent = AZStd::nullopt) override;
|
||||
|
||||
/**
|
||||
* Add a new Link into Prefab System Component and create a unique id for it.
|
||||
@@ -142,8 +146,8 @@ namespace AzToolsFramework
|
||||
* @return A unique id for the new Link.
|
||||
*/
|
||||
LinkId AddLink(
|
||||
const TemplateId& sourceTemplateId,
|
||||
const TemplateId& targetTemplateId,
|
||||
TemplateId sourceTemplateId,
|
||||
TemplateId targetTemplateId,
|
||||
PrefabDomValue::MemberIterator& instanceIterator,
|
||||
InstanceOptionalReference instance) override;
|
||||
|
||||
@@ -157,8 +161,8 @@ namespace AzToolsFramework
|
||||
* @return A unique id for the new Link.
|
||||
*/
|
||||
LinkId CreateLink(
|
||||
const TemplateId& linkTargetId,
|
||||
const TemplateId& linkSourceId,
|
||||
TemplateId linkTargetId,
|
||||
TemplateId linkSourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
const PrefabDomConstReference linkPatches,
|
||||
const LinkId& linkId = InvalidLinkId) override;
|
||||
@@ -181,14 +185,14 @@ namespace AzToolsFramework
|
||||
* @param templateId The id of the template to query.
|
||||
* @return The value of the dirty flag on the template.
|
||||
*/
|
||||
bool IsTemplateDirty(const TemplateId& templateId) override;
|
||||
bool IsTemplateDirty(TemplateId templateId) override;
|
||||
|
||||
/**
|
||||
* Sets the dirty flag of the template to the value provided.
|
||||
* @param templateId The id of the template to flag.
|
||||
* @param dirty The new value of the dirty flag.
|
||||
*/
|
||||
void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) override;
|
||||
void SetTemplateDirtyFlag(TemplateId templateId, bool dirty) override;
|
||||
|
||||
bool AreDirtyTemplatesPresent(TemplateId rootTemplateId) override;
|
||||
|
||||
@@ -200,20 +204,21 @@ namespace AzToolsFramework
|
||||
|
||||
/**
|
||||
* Builds a new Prefab Template out of entities and instances and returns the first instance comprised of
|
||||
* these entities and instances
|
||||
* @param entities A vector of entities that will be used in the new instance. May be empty
|
||||
* these entities and instances.
|
||||
* @param entities A vector of entities that will be used in the new instance. May be empty.
|
||||
* @param instances A vector of Prefab Instances that will be nested in the new instance, will be consumed and moved.
|
||||
* May be empty
|
||||
* @param filePath the path to associate the template of the new instance to.
|
||||
* May be empty.
|
||||
* @param filePath The path to associate the template of the new instance to.
|
||||
* @param containerEntity The container entity for the prefab to be created. It will be created if a nullptr is provided.
|
||||
* @param parent Reference of an instance the created instance will be placed under, if given.
|
||||
* @param shouldCreateLinks The flag indicating if links should be created between the templates of the instance
|
||||
* and its nested instances.
|
||||
* @return A pointer to the newly created instance. nullptr on failure
|
||||
* @return A pointer to the newly created instance. nullptr on failure.
|
||||
*/
|
||||
AZStd::unique_ptr<Instance> CreatePrefab(
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
|
||||
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr,
|
||||
bool ShouldCreateLinks = true) override;
|
||||
InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) override;
|
||||
|
||||
PrefabDom& FindTemplateDom(TemplateId templateId) override;
|
||||
|
||||
@@ -225,18 +230,36 @@ namespace AzToolsFramework
|
||||
*/
|
||||
void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) override;
|
||||
|
||||
void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override;
|
||||
void PropagateTemplateChanges(TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) override;
|
||||
|
||||
/**
|
||||
* Updates all Instances owned by a Template.
|
||||
*
|
||||
* @param templateId The id of the Template owning Instances to update.
|
||||
* @param immediate An optional flag whether to apply the patch immediately (needed for Undo/Redos) or wait until next system tick.
|
||||
* @param instanceToExclude An optional reference to an instance of the template being updated that should not be refreshes as part of propagation.
|
||||
* Defaults to nullopt, which means that all instances will be refreshed.
|
||||
*/
|
||||
void UpdatePrefabInstances(const TemplateId& templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt);
|
||||
void UpdatePrefabInstances(TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt);
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(PrefabSystemComponent);
|
||||
|
||||
/**
|
||||
* Builds a new Prefab Template out of entities and instances and returns the first instance comprised of
|
||||
* these entities and instances.
|
||||
* @param entities A vector of entities that will be used in the new instance. May be empty.
|
||||
* @param instances A vector of Prefab Instances that will be nested in the new instance, will be consumed and moved.
|
||||
* May be empty.
|
||||
* @param filePath The path to associate the template of the new instance to.
|
||||
* @param instance Reference of a pointer to the newly created instance which needs initiation.
|
||||
* @param shouldCreateLinks The flag indicating if links should be created between the templates of the instance
|
||||
* and its nested instances.
|
||||
*/
|
||||
void CreatePrefab(const AZStd::vector<AZ::Entity*>& entities,
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume, AZ::IO::PathView filePath,
|
||||
AZStd::unique_ptr<Instance>& instance, bool shouldCreateLinks);
|
||||
|
||||
/**
|
||||
* Updates all the linked Instances corresponding to the linkIds in the provided queue.
|
||||
* Queue gets populated with more linkId lists as linked instances are updated. Updating stops when the queue is empty.
|
||||
@@ -310,7 +333,7 @@ namespace AzToolsFramework
|
||||
* @param instance The instance that the template was created from. This needs to be editable for inserting linkId into it.
|
||||
* @return bool on whether the operation succeeded
|
||||
*/
|
||||
bool GenerateLinksForNewTemplate(const TemplateId& newTemplateId, Instance& instance);
|
||||
bool GenerateLinksForNewTemplate(TemplateId newTemplateId, Instance& instance);
|
||||
|
||||
/**
|
||||
* Create a unique Template id for newly created Template.
|
||||
|
||||
+13
-10
@@ -29,28 +29,28 @@ namespace AzToolsFramework
|
||||
public:
|
||||
AZ_RTTI(PrefabSystemComponentInterface, "{8E95A029-67F9-4F74-895F-DDBFE29516A0}");
|
||||
|
||||
virtual TemplateReference FindTemplate(const TemplateId& id) = 0;
|
||||
virtual TemplateReference FindTemplate(TemplateId id) = 0;
|
||||
virtual LinkReference FindLink(const LinkId& id) = 0;
|
||||
|
||||
virtual TemplateId AddTemplate(const AZ::IO::Path& filePath, PrefabDom prefabDom) = 0;
|
||||
virtual void UpdateTemplateFilePath(TemplateId templateId, const AZ::IO::PathView& filePath) = 0;
|
||||
virtual void RemoveTemplate(const TemplateId& templateId) = 0;
|
||||
virtual void RemoveTemplate(TemplateId templateId) = 0;
|
||||
virtual void RemoveAllTemplates() = 0;
|
||||
|
||||
virtual LinkId AddLink(const TemplateId& sourceTemplateId, const TemplateId& targetTemplateId,
|
||||
virtual LinkId AddLink(TemplateId sourceTemplateId, TemplateId targetTemplateId,
|
||||
PrefabDomValue::MemberIterator& instanceIterator, InstanceOptionalReference instance) = 0;
|
||||
|
||||
//creates a new Link
|
||||
virtual LinkId CreateLink(
|
||||
const TemplateId& linkTargetId, const TemplateId& linkSourceId, const InstanceAlias& instanceAlias,
|
||||
TemplateId linkTargetId, TemplateId linkSourceId, const InstanceAlias& instanceAlias,
|
||||
const PrefabDomConstReference linkPatches, const LinkId& linkId = InvalidLinkId) = 0;
|
||||
|
||||
virtual void RemoveLink(const LinkId& linkId) = 0;
|
||||
|
||||
virtual TemplateId GetTemplateIdFromFilePath(AZ::IO::PathView filePath) const = 0;
|
||||
|
||||
virtual bool IsTemplateDirty(const TemplateId& templateId) = 0;
|
||||
virtual void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) = 0;
|
||||
virtual bool IsTemplateDirty(TemplateId templateId) = 0;
|
||||
virtual void SetTemplateDirtyFlag(TemplateId templateId, bool dirty) = 0;
|
||||
|
||||
//! Recursive function to check if the template is dirty or if any dirty templates are presents in the links of the template.
|
||||
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
|
||||
@@ -67,13 +67,16 @@ namespace AzToolsFramework
|
||||
|
||||
virtual PrefabDom& FindTemplateDom(TemplateId templateId) = 0;
|
||||
virtual void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) = 0;
|
||||
virtual void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;
|
||||
virtual void PropagateTemplateChanges(TemplateId templateId, bool immediate = false, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;
|
||||
|
||||
virtual AZStd::unique_ptr<Instance> InstantiatePrefab(AZ::IO::PathView filePath) = 0;
|
||||
virtual AZStd::unique_ptr<Instance> InstantiatePrefab(const TemplateId& templateId) = 0;
|
||||
virtual AZStd::unique_ptr<Instance> InstantiatePrefab(
|
||||
AZ::IO::PathView filePath, InstanceOptionalReference parent = AZStd::nullopt) = 0;
|
||||
virtual AZStd::unique_ptr<Instance> InstantiatePrefab(
|
||||
TemplateId templateId, InstanceOptionalReference parent = AZStd::nullopt) = 0;
|
||||
virtual AZStd::unique_ptr<Instance> CreatePrefab(const AZStd::vector<AZ::Entity*>& entities,
|
||||
AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume, AZ::IO::PathView filePath,
|
||||
AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr, bool ShouldCreateLinks = true) = 0;
|
||||
AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr, InstanceOptionalReference parent = AZStd::nullopt,
|
||||
bool shouldCreateLinks = true) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace AzToolsFramework
|
||||
void PrefabUndoInstance::Capture(
|
||||
const PrefabDom& initialState,
|
||||
const PrefabDom& endState,
|
||||
const TemplateId& templateId)
|
||||
TemplateId templateId)
|
||||
{
|
||||
m_templateId = templateId;
|
||||
|
||||
@@ -43,10 +43,15 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabUndoInstance::Undo()
|
||||
{
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId);
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true);
|
||||
}
|
||||
|
||||
void PrefabUndoInstance::Redo()
|
||||
{
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true);
|
||||
}
|
||||
|
||||
void PrefabUndoInstance::RedoBatched()
|
||||
{
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId);
|
||||
}
|
||||
@@ -91,7 +96,7 @@ namespace AzToolsFramework
|
||||
void PrefabUndoEntityUpdate::Undo()
|
||||
{
|
||||
[[maybe_unused]] bool isPatchApplicationSuccessful =
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId);
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_undoPatch, m_templateId, true);
|
||||
|
||||
AZ_Error(
|
||||
"Prefab", isPatchApplicationSuccessful,
|
||||
@@ -102,7 +107,7 @@ namespace AzToolsFramework
|
||||
void PrefabUndoEntityUpdate::Redo()
|
||||
{
|
||||
[[maybe_unused]] bool isPatchApplicationSuccessful =
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId);
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, true);
|
||||
|
||||
AZ_Error(
|
||||
"Prefab", isPatchApplicationSuccessful,
|
||||
@@ -113,7 +118,7 @@ namespace AzToolsFramework
|
||||
void PrefabUndoEntityUpdate::Redo(InstanceOptionalReference instanceToExclude)
|
||||
{
|
||||
[[maybe_unused]] bool isPatchApplicationSuccessful =
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, instanceToExclude);
|
||||
m_instanceToTemplateInterface->PatchTemplate(m_redoPatch, m_templateId, false, instanceToExclude);
|
||||
|
||||
AZ_Error(
|
||||
"Prefab", isPatchApplicationSuccessful,
|
||||
@@ -136,8 +141,8 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
void PrefabUndoInstanceLink::Capture(
|
||||
const TemplateId& targetId,
|
||||
const TemplateId& sourceId,
|
||||
TemplateId targetId,
|
||||
TemplateId sourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
PrefabDom linkPatches,
|
||||
const LinkId linkId)
|
||||
@@ -329,7 +334,7 @@ namespace AzToolsFramework
|
||||
|
||||
//propagate the link changes
|
||||
link->get().UpdateTarget();
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(link->get().GetTargetTemplateId(), instanceToExclude);
|
||||
m_prefabSystemComponentInterface->PropagateTemplateChanges(link->get().GetTargetTemplateId(), false, instanceToExclude);
|
||||
|
||||
//mark as dirty
|
||||
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(link->get().GetTargetTemplateId(), true);
|
||||
|
||||
@@ -49,10 +49,11 @@ namespace AzToolsFramework
|
||||
void Capture(
|
||||
const PrefabDom& initialState,
|
||||
const PrefabDom& endState,
|
||||
const TemplateId& templateId);
|
||||
TemplateId templateId);
|
||||
|
||||
void Undo() override;
|
||||
void Redo() override;
|
||||
void RedoBatched();
|
||||
};
|
||||
|
||||
//! handles entity updates, such as when the values on an entity change
|
||||
@@ -95,8 +96,8 @@ namespace AzToolsFramework
|
||||
|
||||
//capture for add/remove
|
||||
void Capture(
|
||||
const TemplateId& targetId,
|
||||
const TemplateId& sourceId,
|
||||
TemplateId targetId,
|
||||
TemplateId sourceId,
|
||||
const InstanceAlias& instanceAlias,
|
||||
PrefabDom linkPatches = PrefabDom(),
|
||||
const LinkId linkId = InvalidLinkId);
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace AzToolsFramework
|
||||
PrefabUndoInstance* state = aznew Prefab::PrefabUndoInstance(undoMessage);
|
||||
state->Capture(instanceDomBeforeUpdate, instanceDomAfterUpdate, instance.GetTemplateId());
|
||||
state->SetParent(undoBatch);
|
||||
state->Redo();
|
||||
state->RedoBatched();
|
||||
}
|
||||
|
||||
LinkId CreateLink(
|
||||
|
||||
+1
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzToolsFramework/Prefab/Spawnable/PrefabConversionPipeline.h>
|
||||
|
||||
|
||||
+1
@@ -12,6 +12,7 @@
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
|
||||
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/std/containers/unordered_set.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
|
||||
+1
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include "EditorLayerComponent.h"
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
|
||||
+1
@@ -11,6 +11,7 @@
|
||||
#include <AzCore/Script/ScriptSystemBus.h>
|
||||
#include <AzCore/EBus/Results.h>
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/Asset/AssetCatalogBus.h>
|
||||
|
||||
+14
@@ -39,11 +39,20 @@ namespace AzToolsFramework
|
||||
AZ_Assert((m_editorEntityFrameworkInterface != nullptr),
|
||||
"EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction.");
|
||||
|
||||
|
||||
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
|
||||
FocusModeNotificationBus::Handler::BusConnect(editorEntityContextId);
|
||||
|
||||
viewport()->setMouseTracking(true);
|
||||
}
|
||||
|
||||
EntityOutlinerTreeView::~EntityOutlinerTreeView()
|
||||
{
|
||||
FocusModeNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
ClearQueuedMouseEvent();
|
||||
}
|
||||
|
||||
@@ -303,6 +312,11 @@ namespace AzToolsFramework
|
||||
|
||||
StyledTreeView::StartCustomDrag(indexListSorted, supportedActions);
|
||||
}
|
||||
|
||||
void EntityOutlinerTreeView::OnEditorFocusChanged([[maybe_unused]] AZ::EntityId entityId)
|
||||
{
|
||||
viewport()->repaint();
|
||||
}
|
||||
}
|
||||
|
||||
#include <UI/Outliner/moc_EntityOutlinerTreeView.cpp>
|
||||
|
||||
+5
@@ -15,6 +15,7 @@
|
||||
#include <QBasicTimer>
|
||||
#include <QEvent>
|
||||
|
||||
#include <AzToolsFramework/FocusMode/FocusModeNotificationBus.h>
|
||||
#include <AzQtComponents/Components/Widgets/TreeView.h>
|
||||
#endif
|
||||
|
||||
@@ -35,6 +36,7 @@ namespace AzToolsFramework
|
||||
//! of other entities. If the selection updates instantly, this would never be possible.
|
||||
class EntityOutlinerTreeView
|
||||
: public AzQtComponents::StyledTreeView
|
||||
, private FocusModeNotificationBus::Handler
|
||||
{
|
||||
Q_OBJECT;
|
||||
public:
|
||||
@@ -61,6 +63,9 @@ namespace AzToolsFramework
|
||||
void dropEvent(QDropEvent* event) override;
|
||||
void leaveEvent(QEvent* event) override;
|
||||
|
||||
// FocusModeNotificationBus overrides ...
|
||||
void OnEditorFocusChanged(AZ::EntityId entityId) override;
|
||||
|
||||
//! Renders the left side of the item: appropriate background, branch lines, icons.
|
||||
void drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const override;
|
||||
|
||||
|
||||
+1
-1
@@ -1160,7 +1160,7 @@ namespace AzToolsFramework
|
||||
AZStd::string unsavedPrefabFileName = unsavedPrefabFileLabel->property("FilePath").toString().toUtf8().data();
|
||||
AzToolsFramework::Prefab::TemplateId unsavedPrefabTemplateId =
|
||||
s_prefabSystemComponentInterface->GetTemplateIdFromFilePath(unsavedPrefabFileName.data());
|
||||
bool isTemplateSavedSuccessfully = s_prefabLoaderInterface->SaveTemplate(unsavedPrefabTemplateId);
|
||||
[[maybe_unused]] bool isTemplateSavedSuccessfully = s_prefabLoaderInterface->SaveTemplate(unsavedPrefabTemplateId);
|
||||
AZ_Error("Prefab", isTemplateSavedSuccessfully, "Prefab '%s' could not be saved successfully.", unsavedPrefabFileName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
PrefabViewportFocusPathHandler::PrefabViewportFocusPathHandler()
|
||||
{
|
||||
// Get default EntityContextId
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
|
||||
// Connect to Prefab Focus Notifications
|
||||
PrefabFocusNotificationBus::Handler::BusConnect(m_editorEntityContextId);
|
||||
}
|
||||
|
||||
PrefabViewportFocusPathHandler::~PrefabViewportFocusPathHandler()
|
||||
{
|
||||
// Disconnect from Prefab Focus Notifications
|
||||
PrefabFocusNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void PrefabViewportFocusPathHandler::Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton)
|
||||
{
|
||||
// Get reference to the PrefabFocusInterface handler
|
||||
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
if (m_prefabFocusInterface == nullptr)
|
||||
{
|
||||
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabViewportFocusPathHandler construction.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize Widgets
|
||||
m_breadcrumbsWidget = breadcrumbsWidget;
|
||||
m_backButton = backButton;
|
||||
|
||||
// If a part of the path is clicked, focus on that instance
|
||||
connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this,
|
||||
[&](const QString&, int linkIndex)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex);
|
||||
}
|
||||
);
|
||||
|
||||
// The back button will allow user to go one level up
|
||||
connect(m_backButton, &QToolButton::clicked, this,
|
||||
[&]()
|
||||
{
|
||||
if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1)
|
||||
{
|
||||
m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void PrefabViewportFocusPathHandler::OnPrefabFocusChanged()
|
||||
{
|
||||
// Push new Path
|
||||
m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
|
||||
}
|
||||
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
|
||||
#include <AzToolsFramework/Prefab/PrefabFocusNotificationBus.h>
|
||||
|
||||
#include <AzQtComponents/Components/Widgets/BreadCrumbs.h>
|
||||
|
||||
#include <QLayout>
|
||||
#include <QToolButton>
|
||||
|
||||
namespace AzToolsFramework::Prefab
|
||||
{
|
||||
class PrefabFocusInterface;
|
||||
|
||||
class PrefabViewportFocusPathHandler
|
||||
: public PrefabFocusNotificationBus::Handler
|
||||
, private QObject
|
||||
{
|
||||
public:
|
||||
PrefabViewportFocusPathHandler();
|
||||
~PrefabViewportFocusPathHandler();
|
||||
|
||||
void Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton);
|
||||
|
||||
// PrefabFocusNotificationBus overrides ...
|
||||
void OnPrefabFocusChanged() override;
|
||||
|
||||
private:
|
||||
AzQtComponents::BreadCrumbs* m_breadcrumbsWidget = nullptr;
|
||||
QToolButton* m_backButton = nullptr;
|
||||
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
|
||||
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
};
|
||||
} // namespace AzToolsFramework::Prefab
|
||||
+1
@@ -33,6 +33,7 @@ AZ_POP_DISABLE_WARNING
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Asset/AssetTypeInfoBus.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/Asset/SimpleAsset.h>
|
||||
|
||||
+1
@@ -16,6 +16,7 @@
|
||||
#include <QPushButton>
|
||||
#include "PropertyEditorAPI.h"
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Asset/AssetSerializer.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzFramework/Asset/SimpleAsset.h>
|
||||
#include <AzFramework/Asset/AssetCatalogBus.h>
|
||||
|
||||
+3
-2
@@ -119,11 +119,12 @@ namespace AzToolsFramework
|
||||
|
||||
// replace the default input handler with one specific for dealing with
|
||||
// entity selection in the viewport
|
||||
|
||||
EditorInteractionSystemViewportSelectionRequestBus::Event(
|
||||
GetEntityContextId(), &EditorInteractionSystemViewportSelection::SetHandler,
|
||||
[](const EditorVisibleEntityDataCache* entityDataCache)
|
||||
[](const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
|
||||
{
|
||||
return AZStd::make_unique<EditorPickEntitySelection>(entityDataCache);
|
||||
return AZStd::make_unique<EditorPickEntitySelection>(entityDataCache, viewportEditorModeTracker);
|
||||
});
|
||||
|
||||
if (!pickModeEntityContextId.IsNull())
|
||||
|
||||
+4
-2
@@ -22,6 +22,7 @@
|
||||
#include <AZTestShared/Math/MathTestHelpers.h>
|
||||
#include <AZTestShared/Utils/Utils.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/Application/ToolsApplication.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityTransformBus.h>
|
||||
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
|
||||
@@ -166,11 +167,12 @@ namespace UnitTest
|
||||
m_editorActions.Connect();
|
||||
|
||||
const auto viewportHandlerBuilder =
|
||||
[this](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache)
|
||||
[this](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
|
||||
[[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
|
||||
{
|
||||
// create the default viewport (handles ComponentMode)
|
||||
AZStd::unique_ptr<AzToolsFramework::EditorDefaultSelection> defaultSelection =
|
||||
AZStd::make_unique<AzToolsFramework::EditorDefaultSelection>(entityDataCache);
|
||||
AZStd::make_unique<AzToolsFramework::EditorDefaultSelection>(entityDataCache, viewportEditorModeTracker);
|
||||
|
||||
// override the phantom widget so we can use out custom test widget
|
||||
defaultSelection->SetOverridePhantomWidget(&m_editorActions.m_componentModeWidget);
|
||||
|
||||
+7
-1
@@ -9,6 +9,7 @@
|
||||
#include "EditorDefaultSelection.h"
|
||||
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
@@ -19,21 +20,26 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(EditorDefaultSelection, AZ::SystemAllocator, 0)
|
||||
|
||||
EditorDefaultSelection::EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache)
|
||||
EditorDefaultSelection::EditorDefaultSelection(
|
||||
const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
|
||||
: m_phantomWidget(nullptr)
|
||||
, m_entityDataCache(entityDataCache)
|
||||
, m_viewportEditorModeTracker(viewportEditorModeTracker)
|
||||
, m_componentModeCollection(viewportEditorModeTracker)
|
||||
{
|
||||
ActionOverrideRequestBus::Handler::BusConnect(GetEntityContextId());
|
||||
ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusConnect();
|
||||
|
||||
m_manipulatorManager = AZStd::make_shared<AzToolsFramework::ManipulatorManager>(AzToolsFramework::g_mainManipulatorManagerId);
|
||||
m_transformComponentSelection = AZStd::make_unique<EditorTransformComponentSelection>(entityDataCache);
|
||||
m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Default);
|
||||
}
|
||||
|
||||
EditorDefaultSelection::~EditorDefaultSelection()
|
||||
{
|
||||
ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusDisconnect();
|
||||
ActionOverrideRequestBus::Handler::BusDisconnect();
|
||||
m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Default);
|
||||
}
|
||||
|
||||
void EditorDefaultSelection::SetOverridePhantomWidget(QWidget* phantomOverrideWidget)
|
||||
|
||||
+5
-1
@@ -15,6 +15,8 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class ViewportEditorModeTrackerInterface;
|
||||
|
||||
//! The default selection/input handler for the editor (includes handling ComponentMode).
|
||||
class EditorDefaultSelection
|
||||
: public ViewportInteraction::InternalViewportSelectionRequests
|
||||
@@ -25,7 +27,7 @@ namespace AzToolsFramework
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
|
||||
//! @cond
|
||||
explicit EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache);
|
||||
EditorDefaultSelection(const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker);
|
||||
EditorDefaultSelection(const EditorDefaultSelection&) = delete;
|
||||
EditorDefaultSelection& operator=(const EditorDefaultSelection&) = delete;
|
||||
virtual ~EditorDefaultSelection();
|
||||
@@ -110,5 +112,7 @@ namespace AzToolsFramework
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::ManipulatorManager> m_manipulatorManager; //!< The default manipulator manager.
|
||||
ViewportInteraction::MouseInteraction m_currentInteraction; //!< Current mouse interaction to be used for drawing manipulators.
|
||||
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes.
|
||||
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+20
-4
@@ -10,9 +10,24 @@
|
||||
|
||||
#include <AzToolsFramework/ViewportSelection/EditorDefaultSelection.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
#include <AzToolsFramework/ViewportSelection/ViewportEditorModeTracker.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
EditorInteractionSystemComponent::EditorInteractionSystemComponent()
|
||||
: m_viewportEditorMode(AZStd::make_unique<ViewportEditorModeTracker>())
|
||||
{
|
||||
AZ_Assert(AZ::Interface<ViewportEditorModeTrackerInterface>::Get() == nullptr, "Unexpected registration of viewport editor mode tracker.")
|
||||
AZ::Interface<ViewportEditorModeTrackerInterface>::Register(m_viewportEditorMode.get());
|
||||
}
|
||||
|
||||
EditorInteractionSystemComponent::~EditorInteractionSystemComponent()
|
||||
{
|
||||
m_interactionRequests.reset();
|
||||
AZ_Assert(AZ::Interface<ViewportEditorModeTrackerInterface>::Get() != nullptr, "Unexpected unregistration of viewport editor mode tracker.")
|
||||
AZ::Interface<ViewportEditorModeTrackerInterface>::Unregister(m_viewportEditorMode.get());
|
||||
}
|
||||
|
||||
void EditorInteractionSystemComponent::Activate()
|
||||
{
|
||||
EditorInteractionSystemViewportSelectionRequestBus::Handler::BusConnect(GetEntityContextId());
|
||||
@@ -41,7 +56,8 @@ namespace AzToolsFramework
|
||||
return m_interactionRequests->InternalHandleMouseManipulatorInteraction(mouseInteraction);
|
||||
}
|
||||
|
||||
void EditorInteractionSystemComponent::SetHandler(const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder)
|
||||
void EditorInteractionSystemComponent::SetHandler(
|
||||
const ViewportSelectionRequestsBuilderFn& interactionRequestsBuilder)
|
||||
{
|
||||
// when setting a handler, make sure we're connected to the ViewportDebugDisplayEventBus so we
|
||||
// can forward calls to the specific type implementing ViewportSelectionRequests
|
||||
@@ -59,7 +75,7 @@ namespace AzToolsFramework
|
||||
m_entityDataCache = AZStd::make_unique<EditorVisibleEntityDataCache>();
|
||||
m_interactionRequests.reset(); // BusConnect/Disconnect in constructor/destructor,
|
||||
// so have to reset before assigning the new one
|
||||
m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get());
|
||||
m_interactionRequests = interactionRequestsBuilder(m_entityDataCache.get(), m_viewportEditorMode.get());
|
||||
}
|
||||
|
||||
EditorInteractionSystemViewportSelectionRequestBus::Handler::BusConnect(GetEntityContextId());
|
||||
@@ -68,9 +84,9 @@ namespace AzToolsFramework
|
||||
void EditorInteractionSystemComponent::SetDefaultHandler()
|
||||
{
|
||||
SetHandler(
|
||||
[](const EditorVisibleEntityDataCache* entityDataCache)
|
||||
[](const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
|
||||
{
|
||||
return AZStd::make_unique<EditorDefaultSelection>(entityDataCache);
|
||||
return AZStd::make_unique<EditorDefaultSelection>(entityDataCache, viewportEditorModeTracker);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -14,6 +14,8 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class ViewportEditorModeTracker;
|
||||
|
||||
//! System Component to wrap active input handler.
|
||||
//! EditorInteractionSystemComponent is notified of viewport mouse events from RenderViewport
|
||||
//! and forwards them to a concrete implementation of ViewportSelectionRequests.
|
||||
@@ -26,6 +28,9 @@ namespace AzToolsFramework
|
||||
public:
|
||||
AZ_COMPONENT(EditorInteractionSystemComponent, "{146D0317-AF42-45AB-A953-F54198525DD5}")
|
||||
|
||||
EditorInteractionSystemComponent();
|
||||
~EditorInteractionSystemComponent();
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// EditorInteractionSystemViewportSelectionRequestBus
|
||||
@@ -54,5 +59,7 @@ namespace AzToolsFramework
|
||||
AZStd::unique_ptr<InternalViewportSelectionRequests> m_interactionRequests; //!< Hold a concrete implementation of
|
||||
//!< ViewportSelectionRequests to handle viewport
|
||||
//!< input and drawing for the Editor.
|
||||
|
||||
AZStd::unique_ptr<ViewportEditorModeTracker> m_viewportEditorMode; //!< Editor mode tracker for each viewport.
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+3
-2
@@ -17,6 +17,7 @@
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EditorVisibleEntityDataCache;
|
||||
class ViewportEditorModeTrackerInterface;
|
||||
|
||||
//! Bus to handle all mouse events originating from the viewport.
|
||||
//! Coordinated by the EditorInteractionSystemComponent
|
||||
@@ -32,8 +33,8 @@ namespace AzToolsFramework
|
||||
};
|
||||
|
||||
//! Alias for factory function to create a new type implementing the ViewportSelectionRequests interface.
|
||||
using ViewportSelectionRequestsBuilderFn =
|
||||
AZStd::function<AZStd::unique_ptr<ViewportInteraction::InternalViewportSelectionRequests>(const EditorVisibleEntityDataCache*)>;
|
||||
using ViewportSelectionRequestsBuilderFn = AZStd::function<AZStd::unique_ptr<ViewportInteraction::InternalViewportSelectionRequests>(
|
||||
const EditorVisibleEntityDataCache*, ViewportEditorModeTrackerInterface*)>;
|
||||
|
||||
//! Interface for system component implementing the ViewportSelectionRequests interface.
|
||||
//! This interface also includes a setter to set a custom handler also implementing
|
||||
|
||||
+7
-1
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "EditorPickEntitySelection.h"
|
||||
|
||||
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <QApplication>
|
||||
|
||||
@@ -15,9 +16,12 @@ namespace AzToolsFramework
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(EditorPickEntitySelection, AZ::SystemAllocator, 0)
|
||||
|
||||
EditorPickEntitySelection::EditorPickEntitySelection(const EditorVisibleEntityDataCache* entityDataCache)
|
||||
EditorPickEntitySelection::EditorPickEntitySelection(
|
||||
const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
|
||||
: m_editorHelpers(AZStd::make_unique<EditorHelpers>(entityDataCache))
|
||||
, m_viewportEditorModeTracker(viewportEditorModeTracker)
|
||||
{
|
||||
m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Pick);
|
||||
}
|
||||
|
||||
EditorPickEntitySelection::~EditorPickEntitySelection()
|
||||
@@ -26,6 +30,8 @@ namespace AzToolsFramework
|
||||
{
|
||||
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, m_hoveredEntityId, false);
|
||||
}
|
||||
|
||||
m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Pick);
|
||||
}
|
||||
|
||||
// note: entityIdUnderCursor is the authoritative entityId we get each frame by querying
|
||||
|
||||
+5
-1
@@ -13,6 +13,8 @@
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class ViewportEditorModeTrackerInterface;
|
||||
|
||||
//! Viewport interaction that will handle assigning an entity in the viewport to
|
||||
//! an entity field in the entity inspector.
|
||||
class EditorPickEntitySelection : public ViewportInteraction::InternalViewportSelectionRequests
|
||||
@@ -20,7 +22,8 @@ namespace AzToolsFramework
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR_DECL
|
||||
|
||||
EditorPickEntitySelection(const EditorVisibleEntityDataCache* entityDataCache);
|
||||
EditorPickEntitySelection(
|
||||
const EditorVisibleEntityDataCache* entityDataCache, ViewportEditorModeTrackerInterface* viewportEditorModeTracker);
|
||||
~EditorPickEntitySelection();
|
||||
|
||||
private:
|
||||
@@ -32,5 +35,6 @@ namespace AzToolsFramework
|
||||
AZStd::unique_ptr<EditorHelpers> m_editorHelpers; //!< Editor visualization of entities (icons, shapes, debug visuals etc).
|
||||
AZ::EntityId m_hoveredEntityId; //!< What EntityId is the mouse currently hovering over (if any).
|
||||
AZ::EntityId m_cachedEntityIdUnderCursor; //!< Store the EntityId on each mouse move for use in Display.
|
||||
ViewportEditorModeTrackerInterface* m_viewportEditorModeTracker = nullptr; //!< Tracker for activating/deactivating viewport editor modes.
|
||||
};
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
-16
@@ -45,22 +45,6 @@ namespace AzToolsFramework
|
||||
return m_editorModes[static_cast<AZ::u32>(mode)];
|
||||
}
|
||||
|
||||
void ViewportEditorModeTracker::RegisterInterface()
|
||||
{
|
||||
if (AZ::Interface<ViewportEditorModeTrackerInterface>::Get() == nullptr)
|
||||
{
|
||||
AZ::Interface<ViewportEditorModeTrackerInterface>::Register(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportEditorModeTracker::UnregisterInterface()
|
||||
{
|
||||
if (AZ::Interface<ViewportEditorModeTrackerInterface>::Get() != nullptr)
|
||||
{
|
||||
AZ::Interface<ViewportEditorModeTrackerInterface>::Unregister(this);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> ViewportEditorModeTracker::ActivateMode(
|
||||
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode)
|
||||
{
|
||||
|
||||
-6
@@ -41,12 +41,6 @@ namespace AzToolsFramework
|
||||
: public ViewportEditorModeTrackerInterface
|
||||
{
|
||||
public:
|
||||
//! Registers this object with the AZ::Interface.
|
||||
void RegisterInterface();
|
||||
|
||||
//! Unregisters this object with the AZ::Interface.
|
||||
void UnregisterInterface();
|
||||
|
||||
// ViewportEditorModeTrackerInterface overrides ...
|
||||
AZ::Outcome<void, AZStd::string> ActivateMode(const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) override;
|
||||
AZ::Outcome<void, AZStd::string> DeactivateMode(const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) override;
|
||||
|
||||
@@ -29,13 +29,29 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
|
||||
void ButtonGroup::SetHighlightedButton(ButtonId buttonId)
|
||||
{
|
||||
if (buttonId == m_highlightedButtonId) // the requested button is highlighted, so do nothing.
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto buttonEntry = m_buttons.find(buttonId); buttonEntry != m_buttons.end())
|
||||
{
|
||||
for (auto& button : m_buttons)
|
||||
{
|
||||
button.second->m_state = Button::State::Deselected;
|
||||
}
|
||||
ClearHighlightedButton();
|
||||
buttonEntry->second->m_state = Button::State::Selected;
|
||||
m_highlightedButtonId = buttonId;
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonGroup::ClearHighlightedButton()
|
||||
{
|
||||
if (m_highlightedButtonId == InvalidButtonId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (auto buttonEntry = m_buttons.find(m_highlightedButtonId); buttonEntry != m_buttons.end())
|
||||
{
|
||||
buttonEntry->second->m_state = Button::State::Deselected;
|
||||
m_highlightedButtonId = InvalidButtonId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
|
||||
|
||||
namespace AzToolsFramework::ViewportUi::Internal
|
||||
@@ -24,6 +25,7 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
~ButtonGroup() = default;
|
||||
|
||||
void SetHighlightedButton(ButtonId buttonId);
|
||||
void ClearHighlightedButton();
|
||||
|
||||
void SetViewportUiElementId(ViewportUiElementId id);
|
||||
ViewportUiElementId GetViewportUiElementId() const;
|
||||
@@ -39,5 +41,6 @@ namespace AzToolsFramework::ViewportUi::Internal
|
||||
AZ::Event<ButtonId> m_buttonTriggeredEvent;
|
||||
ViewportUiElementId m_viewportUiId;
|
||||
AZStd::unordered_map<ButtonId, AZStd::unique_ptr<Button>> m_buttons;
|
||||
ButtonId m_highlightedButtonId = InvalidButtonId;
|
||||
};
|
||||
} // namespace AzToolsFramework::ViewportUi::Internal
|
||||
|
||||
@@ -50,6 +50,16 @@ namespace AzToolsFramework::ViewportUi
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportUiManager::ClearClusterActiveButton(ClusterId clusterId)
|
||||
{
|
||||
if (auto clusterIt = m_clusterButtonGroups.find(clusterId); clusterIt != m_clusterButtonGroups.end())
|
||||
{
|
||||
auto cluster = clusterIt->second;
|
||||
cluster->ClearHighlightedButton();
|
||||
UpdateButtonGroupUi(cluster.get());
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportUiManager::SetSwitcherActiveButton(const SwitcherId switcherId, const ButtonId buttonId)
|
||||
{
|
||||
if (auto switcherIt = m_switcherButtonGroups.find(switcherId); switcherIt != m_switcherButtonGroups.end())
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace AzToolsFramework::ViewportUi
|
||||
const ClusterId CreateCluster(Alignment align) override;
|
||||
const SwitcherId CreateSwitcher(Alignment align) override;
|
||||
void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) override;
|
||||
void ClearClusterActiveButton(ClusterId clusterId) override;
|
||||
void SetSwitcherActiveButton(SwitcherId switcherId, ButtonId buttonId) override;
|
||||
void SetClusterButtonLocked(ClusterId clusterId, ButtonId buttonId, bool isLocked) override;
|
||||
void SetClusterButtonTooltip(ClusterId clusterId, ButtonId buttonId, const AZStd::string& tooltip) override;
|
||||
|
||||
@@ -59,6 +59,8 @@ namespace AzToolsFramework::ViewportUi
|
||||
virtual const SwitcherId CreateSwitcher(Alignment align) = 0;
|
||||
//! Sets the active button of the cluster. This is the button which will display as highlighted.
|
||||
virtual void SetClusterActiveButton(ClusterId clusterId, ButtonId buttonId) = 0;
|
||||
//! Clears the active button of the cluster if one is active. The button will no longer display as highlighted.
|
||||
virtual void ClearClusterActiveButton(ClusterId clusterId) = 0;
|
||||
//! Sets the active button of the switcher. This is the button which has a text label.
|
||||
virtual void SetSwitcherActiveButton(SwitcherId clusterId, ButtonId buttonId) = 0;
|
||||
//! Adds a locked overlay to the cluster button's icon.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
AssetEditor/AssetEditorBus.cpp
|
||||
AssetEditor/AssetEditorBus.h
|
||||
AssetEditor/AssetEditorToolbar.ui
|
||||
AssetEditor/AssetEditorStatusBar.ui
|
||||
@@ -150,6 +151,7 @@ set(FILES
|
||||
Fingerprinting/TypeFingerprinter.h
|
||||
Fingerprinting/TypeFingerprinter.cpp
|
||||
FocusMode/FocusModeInterface.h
|
||||
FocusMode/FocusModeNotificationBus.h
|
||||
FocusMode/FocusModeSystemComponent.h
|
||||
FocusMode/FocusModeSystemComponent.cpp
|
||||
Logger/TraceLogger.cpp
|
||||
@@ -635,6 +637,7 @@ set(FILES
|
||||
Prefab/PrefabFocusHandler.h
|
||||
Prefab/PrefabFocusHandler.cpp
|
||||
Prefab/PrefabFocusInterface.h
|
||||
Prefab/PrefabFocusNotificationBus.h
|
||||
Prefab/PrefabIdTypes.h
|
||||
Prefab/PrefabLoader.h
|
||||
Prefab/PrefabLoader.cpp
|
||||
@@ -733,6 +736,8 @@ set(FILES
|
||||
UI/Prefab/PrefabIntegrationInterface.h
|
||||
UI/Prefab/PrefabUiHandler.h
|
||||
UI/Prefab/PrefabUiHandler.cpp
|
||||
UI/Prefab/PrefabViewportFocusPathHandler.h
|
||||
UI/Prefab/PrefabViewportFocusPathHandler.cpp
|
||||
PythonTerminal/ScriptHelpDialog.cpp
|
||||
PythonTerminal/ScriptHelpDialog.h
|
||||
PythonTerminal/ScriptHelpDialog.ui
|
||||
|
||||
-230
@@ -1,230 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
[[maybe_unused]] static const char ErrorChannel[] = "ArchiveComponent_Linux";
|
||||
|
||||
static const char ZipExePath[] = R"(/usr/bin/zip)";
|
||||
static const char UnzipExePath[] = R"(/usr/bin/unzip)";
|
||||
|
||||
static const char CreateArchiveCmd[] = "-r \"%s\" . -i *";
|
||||
|
||||
static const char ExtractArchiveCmd[] = R"(-o "%s" -d "%s")";
|
||||
|
||||
static const char AddFileCmd[] = R"("%s" "%s")";
|
||||
|
||||
static const char ExtractFileCmd[] = R"(%s "%s" %s)";
|
||||
static const char ExtractFileDestination[] = R"(%s "%s" "%s" -d "%s")";
|
||||
static const char ExtractOverwrite[] = "-o";
|
||||
static const char ExtractSkipExisting[] = "-n";
|
||||
static const char ListFilesInArchiveCmd[] = "-l %s";
|
||||
|
||||
AZStd::string GetZipExePath()
|
||||
{
|
||||
return ZipExePath;
|
||||
}
|
||||
|
||||
AZStd::string GetUnzipExePath()
|
||||
{
|
||||
return UnzipExePath;
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> MakePath(const AZStd::string& path)
|
||||
{
|
||||
// Create the folder if it does not already exist
|
||||
if (!AZ::IO::FileIOBase::GetInstance()->Exists(path.c_str()))
|
||||
{
|
||||
auto result = AZ::IO::FileIOBase::GetInstance()->CreatePath(path.c_str());
|
||||
if (!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Path creation failed. Input path: %s \n", path.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success(path);
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> MakeCreateArchivePath(const AZStd::string& archivePath)
|
||||
{
|
||||
// Remove the file name from the input path
|
||||
// /some/folder/path/archive.zip -> /some/folder/path/
|
||||
AZStd::string strippedArchivePath = archivePath;
|
||||
AzFramework::StringFunc::Path::StripFullName(strippedArchivePath);
|
||||
|
||||
if (strippedArchivePath.empty())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Stripped path name is empty. Cancelling path creation. Input path: %s\n", archivePath.c_str()));
|
||||
}
|
||||
|
||||
return MakePath(strippedArchivePath);
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> MakeExtractArchivePath(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool includeRoot)
|
||||
{
|
||||
if(!includeRoot)
|
||||
{
|
||||
// Create the folder for the input destination path with no modifications
|
||||
// /path/to/destination/
|
||||
return MakePath(destinationPath);
|
||||
}
|
||||
|
||||
// Get the name of the input archive. This will be the name of the root folder for the archive extraction
|
||||
// /some/folder/path/archive.zip -> archive
|
||||
AZStd::string zipFileName;
|
||||
bool result = AzFramework::StringFunc::Path::GetFileName(archivePath.c_str(), zipFileName);
|
||||
if(!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to get name of zip file from the archive path. Cancelling path creation. \n Input Archive Path: %s \n", archivePath.c_str()));
|
||||
}
|
||||
|
||||
// Append the root folder name to the end of the destination path
|
||||
// /path/to/destination/ + archive -> /path/to/destination/archive
|
||||
AZStd::string destinationPathWithRoot;
|
||||
result = AzFramework::StringFunc::Path::Join(destinationPath.c_str(), zipFileName.c_str(), destinationPathWithRoot);
|
||||
if(!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to append zip file name to the destination path. Cancelling path creation. \n Destination Path: %s \n Zip file name: %s \n", destinationPath.c_str(), zipFileName.c_str()));
|
||||
}
|
||||
|
||||
// Append a separator so that it is formatted like a folder
|
||||
// /path/to/destination/archive -> /path/to/destination/archive/
|
||||
AzFramework::StringFunc::Path::AppendSeparator(destinationPathWithRoot);
|
||||
return MakePath(destinationPathWithRoot);
|
||||
}
|
||||
|
||||
AZStd::string GetCreateArchiveCommand(const AZStd::string& archivePath, const AZStd::string& dirToArchive)
|
||||
{
|
||||
auto pathCreationResult = MakeCreateArchivePath(archivePath);
|
||||
if (!pathCreationResult)
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, "%s", pathCreationResult.GetError().c_str());
|
||||
return "";
|
||||
}
|
||||
AZ_UNUSED(dirToArchive);
|
||||
return AZStd::string::format(CreateArchiveCmd, archivePath.c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetExtractArchiveCommand(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool includeRoot)
|
||||
{
|
||||
auto pathCreationResult = MakeExtractArchivePath(archivePath, destinationPath, includeRoot);
|
||||
if (!pathCreationResult)
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, "%s", pathCreationResult.GetError().c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
return AZStd::string::format(ExtractArchiveCmd, archivePath.c_str(), pathCreationResult.GetValue().c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetAddFilesToArchiveCommand(const AZStd::string& /*archivePath*/, const AZStd::string& /*listFilePath*/)
|
||||
{
|
||||
// Adding files into a archive using a list file is not currently supported
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IsAddFilesToArchiveCommandSupported()
|
||||
{
|
||||
// Adding files into a archive using a list file is not currently supported
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::string GetAddFileToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& file)
|
||||
{
|
||||
if (!MakeCreateArchivePath(archivePath).IsSuccess())
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, "Unable to make path for ( %s ).\n", archivePath.c_str());
|
||||
return {};
|
||||
}
|
||||
return AZStd::string::format(AddFileCmd, archivePath.c_str(), file.c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetExtractFileCommand(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite)
|
||||
{
|
||||
AZStd::string commandLineArgs;
|
||||
if (destinationPath.empty())
|
||||
{
|
||||
// Extract file in archive from archive path to the current directory, overwriting a file of the same name that exists there.
|
||||
commandLineArgs = AZStd::string::format(ExtractFileCmd, overWrite ? ExtractOverwrite : ExtractSkipExisting, archivePath.c_str(), fileInArchive.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!MakePath(destinationPath).IsSuccess())
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, "Unable to make path ( %s ).\n", destinationPath.c_str());
|
||||
return {};
|
||||
}
|
||||
// Extract file in archive from archive path to destinationPath, overwriting a file of the same name that exists there.
|
||||
commandLineArgs = AZStd::string::format(ExtractFileDestination, overWrite ? ExtractOverwrite : ExtractSkipExisting, archivePath.c_str(), fileInArchive.c_str(), destinationPath.c_str());
|
||||
}
|
||||
|
||||
return commandLineArgs;
|
||||
}
|
||||
|
||||
AZStd::string GetListFilesInArchiveCommand(const AZStd::string& archivePath)
|
||||
{
|
||||
AZStd::string commandLineArgs = AZStd::string::format(ListFilesInArchiveCmd, archivePath.c_str());
|
||||
return commandLineArgs;
|
||||
}
|
||||
|
||||
/*
|
||||
Sample Console Output of the unzip list command
|
||||
|
||||
Archive: /var/folders/1q/12nyzqc913qgm532y2c98mnm6w4_qv/T/ArchiveTests-ra8oMy/TestArchive.pak
|
||||
Length Date Time Name
|
||||
--------- ---------- ----- ----
|
||||
0 10-14-2019 15:22 testfolder/
|
||||
1 10-14-2019 15:22 testfolder/folderfile.txt
|
||||
1 10-14-2019 15:22 basicfile.txt
|
||||
1 10-14-2019 15:22 basicfile2.txt
|
||||
0 10-14-2019 15:22 testfolder2/
|
||||
1 10-14-2019 15:22 testfolder2/sharedfolderfile2.txt
|
||||
1 10-14-2019 15:22 testfolder2/sharedfolderfile.txt
|
||||
0 10-14-2019 15:22 testfolder3/
|
||||
0 10-14-2019 15:22 testfolder3/testfolder4/
|
||||
1 10-14-2019 15:22 testfolder3/testfolder4/depthfile.bat
|
||||
--------- -------
|
||||
6 10 files
|
||||
*/
|
||||
|
||||
void ParseConsoleOutputFromListFilesInArchive(const AZStd::string& consoleOutput, AZStd::vector<AZStd::string>& fileEntries)
|
||||
{
|
||||
AZStd::vector<AZStd::string> fileEntryData;
|
||||
AzFramework::StringFunc::Tokenize(consoleOutput.c_str(), fileEntryData, "\n");
|
||||
int startingLineIdx = 3; // first line that might contain the file name
|
||||
for (size_t lineIdx = startingLineIdx; lineIdx < fileEntryData.size(); ++lineIdx)
|
||||
{
|
||||
AZStd::string& line = fileEntryData[lineIdx];
|
||||
AZStd::vector<AZStd::string> lineEntryData;
|
||||
AzFramework::StringFunc::Tokenize(line.c_str(), lineEntryData, " ");
|
||||
AZStd::string& fileName = lineEntryData.back();
|
||||
|
||||
if(fileName.back() == AZ_CORRECT_FILESYSTEM_SEPARATOR)
|
||||
{
|
||||
// if the filename ends with a separator
|
||||
// than it indicates that this is a directory
|
||||
continue;
|
||||
}
|
||||
|
||||
if(fileName.compare("-------") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fileEntries.emplace_back(fileName);
|
||||
}
|
||||
}
|
||||
} // namespace Platform
|
||||
} // namespace AzToolsFramework
|
||||
@@ -7,5 +7,4 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
AzToolsFramework/Archive/ArchiveComponent_Linux.cpp
|
||||
)
|
||||
|
||||
-263
@@ -1,263 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
const char ErrorChannel[] = "ArchiveComponent_OSX";
|
||||
|
||||
const char ZipExePath[] = R"(/usr/bin/zip)";
|
||||
const char UnzipExePath[] = R"(/usr/bin/unzip)";
|
||||
|
||||
// v Requires investigation, the correct cmd should be R"(-r "%s" "%s/")" but tests fail
|
||||
const char CreateArchiveCmd[] = R"(-r "%s" .)";
|
||||
|
||||
const char ExtractArchiveCmd[] = R"(-o "%s" -d "%s")";
|
||||
|
||||
const char AddFileCmd[] = R"("%s" "%s" -X)";
|
||||
const char AddFilesCmd[] = R"("%s" -X %s)";
|
||||
|
||||
const char ExtractFileCmd[] = R"(%s "%s" %s)";
|
||||
const char ExtractFileDestination[] = R"(%s "%s" "%s" -d "%s")";
|
||||
const char ExtractOverwrite[] = "-o";
|
||||
const char ExtractSkipExisting[] = "-n";
|
||||
const char ListFilesInArchiveCmd[] = "-l %s";
|
||||
|
||||
AZStd::string GetZipExePath()
|
||||
{
|
||||
return ZipExePath;
|
||||
}
|
||||
|
||||
AZStd::string GetUnzipExePath()
|
||||
{
|
||||
return UnzipExePath;
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> MakePath(const AZStd::string& path)
|
||||
{
|
||||
// Create the folder if it does not already exist
|
||||
if (!AZ::IO::FileIOBase::GetInstance()->Exists(path.c_str()))
|
||||
{
|
||||
auto result = AZ::IO::FileIOBase::GetInstance()->CreatePath(path.c_str());
|
||||
if (!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Path creation failed. Input path: %s \n", path.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
return AZ::Success(path);
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> MakeCreateArchivePath(const AZStd::string& archivePath)
|
||||
{
|
||||
// Remove the file name from the input path
|
||||
// /some/folder/path/archive.zip -> /some/folder/path/
|
||||
AZStd::string strippedArchivePath = archivePath;
|
||||
AzFramework::StringFunc::Path::StripFullName(strippedArchivePath);
|
||||
|
||||
if (strippedArchivePath.empty())
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Stripped path name is empty. Cancelling path creation. Input path: %s\n", archivePath.c_str()));
|
||||
}
|
||||
|
||||
return MakePath(strippedArchivePath);
|
||||
}
|
||||
|
||||
AZ::Outcome<AZStd::string, AZStd::string> MakeExtractArchivePath(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool includeRoot)
|
||||
{
|
||||
if(!includeRoot)
|
||||
{
|
||||
// Create the folder for the input destination path with no modifications
|
||||
// /path/to/destination/
|
||||
return MakePath(destinationPath);
|
||||
}
|
||||
|
||||
// Get the name of the input archive. This will be the name of the root folder for the archive extraction
|
||||
// /some/folder/path/archive.zip -> archive
|
||||
AZStd::string zipFileName;
|
||||
bool result = AzFramework::StringFunc::Path::GetFileName(archivePath.c_str(), zipFileName);
|
||||
if(!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to get name of zip file from the archive path. Cancelling path creation. \n Input Archive Path: %s \n", archivePath.c_str()));
|
||||
}
|
||||
|
||||
// Append the root folder name to the end of the destination path
|
||||
// /path/to/destination/ + archive -> /path/to/destination/archive
|
||||
AZStd::string destinationPathWithRoot;
|
||||
result = AzFramework::StringFunc::Path::Join(destinationPath.c_str(), zipFileName.c_str(), destinationPathWithRoot);
|
||||
if(!result)
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format("Failed to append zip file name to the destination path. Cancelling path creation. \n Destination Path: %s \n Zip file name: %s \n", destinationPath.c_str(), zipFileName.c_str()));
|
||||
}
|
||||
|
||||
// Append a separator so that it is formatted like a folder
|
||||
// /path/to/destination/archive -> /path/to/destination/archive/
|
||||
AzFramework::StringFunc::Path::AppendSeparator(destinationPathWithRoot);
|
||||
return MakePath(destinationPathWithRoot);
|
||||
}
|
||||
|
||||
AZStd::string GetCreateArchiveCommand(const AZStd::string& archivePath, const AZStd::string& dirToArchive)
|
||||
{
|
||||
auto pathCreationResult = MakeCreateArchivePath(archivePath);
|
||||
if (!pathCreationResult.IsSuccess())
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, pathCreationResult.GetError().c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
// LY-116692. Requires proper investigation, the correct format should be:
|
||||
// AZStd::string::format(CreateArchiveCmd, archivePath.c_str(), dirToArchive.c_str());
|
||||
// but unit test ArchiveTest.ListFilesInArchiveBlocking_FilesAtThreeDepths_FilesFound fails
|
||||
AZ_UNUSED(dirToArchive);
|
||||
return AZStd::string::format(CreateArchiveCmd, archivePath.c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetExtractArchiveCommand(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool includeRoot)
|
||||
{
|
||||
auto pathCreationResult = MakeExtractArchivePath(archivePath, destinationPath, includeRoot);
|
||||
if (!pathCreationResult)
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, pathCreationResult.GetError().c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
return AZStd::string::format(ExtractArchiveCmd, archivePath.c_str(), pathCreationResult.GetValue().c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetAddFilesToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& listFilePath)
|
||||
{
|
||||
auto pathCreationResult = MakeCreateArchivePath(archivePath);
|
||||
if (!pathCreationResult)
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, pathCreationResult.GetError().c_str());
|
||||
return "";
|
||||
}
|
||||
AZStd::string fileListStr;
|
||||
|
||||
{
|
||||
AZ::IO::FileIOStream fileStream(listFilePath.c_str(), AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeText);
|
||||
if (fileStream.IsOpen())
|
||||
{
|
||||
AZ::IO::SizeType length = fileStream.GetLength();
|
||||
AZStd::vector<char> charBuffer;
|
||||
charBuffer.resize_no_construct(length + 1);
|
||||
|
||||
fileStream.Read(length, charBuffer.data());
|
||||
charBuffer.back() = 0;
|
||||
|
||||
fileListStr.append("\"");
|
||||
fileListStr.insert(1, charBuffer.data());
|
||||
AzFramework::StringFunc::Replace(fileListStr, "\n", "\" \"");
|
||||
fileListStr.append("\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, "Unable to read list file ( %s ) \n", listFilePath.c_str());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return AZStd::string::format(AddFilesCmd, archivePath.c_str(), fileListStr.c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetAddFileToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& file)
|
||||
{
|
||||
auto pathCreationResult = MakeCreateArchivePath(archivePath);
|
||||
if (!pathCreationResult)
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, pathCreationResult.GetError().c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
return AZStd::string::format(AddFileCmd, archivePath.c_str(), file.c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetExtractFileCommand(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite)
|
||||
{
|
||||
AZStd::string commandLineArgs;
|
||||
if (destinationPath.empty())
|
||||
{
|
||||
// Extract file in archive from archive path to the current directory, overwriting a file of the same name that exists there.
|
||||
commandLineArgs = AZStd::string::format(ExtractFileCmd, overWrite ? ExtractOverwrite : ExtractSkipExisting, archivePath.c_str(), fileInArchive.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!MakePath(destinationPath).IsSuccess())
|
||||
{
|
||||
AZ_Error(ErrorChannel, false, "Unable to make path ( %s ).\n", destinationPath.c_str());
|
||||
return "";
|
||||
}
|
||||
// Extract file in archive from archive path to destinationPath, overwriting a file of the same name that exists there.
|
||||
commandLineArgs = AZStd::string::format(ExtractFileDestination, overWrite ? ExtractOverwrite : ExtractSkipExisting, archivePath.c_str(), fileInArchive.c_str(), destinationPath.c_str());
|
||||
}
|
||||
|
||||
return commandLineArgs;
|
||||
}
|
||||
|
||||
AZStd::string GetListFilesInArchiveCommand(const AZStd::string& archivePath)
|
||||
{
|
||||
AZStd::string commandLineArgs = AZStd::string::format(ListFilesInArchiveCmd, archivePath.c_str());
|
||||
return commandLineArgs;
|
||||
}
|
||||
|
||||
/*
|
||||
Sample Console Output of the unzip list command
|
||||
|
||||
Archive: /var/folders/1q/12nyzqc913qgm532y2c98mnm6w4_qv/T/ArchiveTests-ra8oMy/TestArchive.pak
|
||||
Length Date Time Name
|
||||
--------- ---------- ----- ----
|
||||
0 10-14-2019 15:22 testfolder/
|
||||
1 10-14-2019 15:22 testfolder/folderfile.txt
|
||||
1 10-14-2019 15:22 basicfile.txt
|
||||
1 10-14-2019 15:22 basicfile2.txt
|
||||
0 10-14-2019 15:22 testfolder2/
|
||||
1 10-14-2019 15:22 testfolder2/sharedfolderfile2.txt
|
||||
1 10-14-2019 15:22 testfolder2/sharedfolderfile.txt
|
||||
0 10-14-2019 15:22 testfolder3/
|
||||
0 10-14-2019 15:22 testfolder3/testfolder4/
|
||||
1 10-14-2019 15:22 testfolder3/testfolder4/depthfile.bat
|
||||
--------- -------
|
||||
6 10 files
|
||||
*/
|
||||
|
||||
void ParseConsoleOutputFromListFilesInArchive(const AZStd::string& consoleOutput, AZStd::vector<AZStd::string>& fileEntries)
|
||||
{
|
||||
AZStd::vector<AZStd::string> fileEntryData;
|
||||
AzFramework::StringFunc::Tokenize(consoleOutput.c_str(), fileEntryData, "\n");
|
||||
int startingLineIdx = 3; // first line that might contain the file name
|
||||
for (size_t lineIdx = startingLineIdx; lineIdx < fileEntryData.size(); ++lineIdx)
|
||||
{
|
||||
AZStd::string& line = fileEntryData[lineIdx];
|
||||
AZStd::vector<AZStd::string> lineEntryData;
|
||||
AzFramework::StringFunc::Tokenize(line.c_str(), lineEntryData, " ");
|
||||
AZStd::string& fileName = lineEntryData.back();
|
||||
|
||||
if(fileName.back() == AZ_CORRECT_FILESYSTEM_SEPARATOR)
|
||||
{
|
||||
// if the filename ends with a separator
|
||||
// than it indicates that this is a directory
|
||||
continue;
|
||||
}
|
||||
|
||||
if(fileName.compare("-------") == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fileEntries.emplace_back(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Platform
|
||||
} // namespace AzToolsFramework
|
||||
@@ -7,5 +7,4 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
AzToolsFramework/Archive/ArchiveComponent_Mac.cpp
|
||||
)
|
||||
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/Archive/ArchiveComponent.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Platform
|
||||
{
|
||||
const char CreateArchiveCmd[] = R"(a -tzip -mx=1 "%s" -r "%s\*")";
|
||||
|
||||
// -aos is for skipping extract on existing files
|
||||
const char ExtractArchiveCmd[] = R"(x -mmt=off "%s" -o"%s\*" -aos)";
|
||||
const char ExtractArchiveWithoutRootCmd[] = R"(x -mmt=off "%s" -o"%s" -aos)";
|
||||
const char AddFilesCmd[] = R"(a -tzip "%s" @"%s")";
|
||||
const char AddFileCmd[] = R"(a -tzip "%s" "%s")";
|
||||
const char ExtractFileCmd[] = R"(e -mmt=off "%s" "%s" %s)";
|
||||
const char ExtractFileDestination[] = R"(e -mmt=off "%s" -o"%s" "%s" %s)";
|
||||
const char ExtractOverwrite[] = "-aoa";
|
||||
const char ExtractSkipExisting[] = "-aos";
|
||||
const char ListFilesInArchiveCmd[] = R"(l -r -slt "%s")";
|
||||
|
||||
AZStd::string Get7zExePath()
|
||||
{
|
||||
const char* rootPath = nullptr;
|
||||
AZ::ComponentApplicationBus::BroadcastResult(rootPath, &AZ::ComponentApplicationRequests::GetEngineRoot);
|
||||
AZStd::string exePath;
|
||||
AzFramework::StringFunc::Path::ConstructFull(rootPath, "Tools", "7za", ".exe", exePath);
|
||||
return exePath;
|
||||
}
|
||||
|
||||
AZStd::string GetZipExePath()
|
||||
{
|
||||
return Get7zExePath();
|
||||
}
|
||||
|
||||
AZStd::string GetUnzipExePath()
|
||||
{
|
||||
return Get7zExePath();
|
||||
}
|
||||
|
||||
AZStd::string GetCreateArchiveCommand(const AZStd::string& archivePath, const AZStd::string& dirToArchive)
|
||||
{
|
||||
return AZStd::string::format(CreateArchiveCmd, archivePath.c_str(), dirToArchive.c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetExtractArchiveCommand(const AZStd::string& archivePath, const AZStd::string& destinationPath, bool includeRoot)
|
||||
{
|
||||
if (includeRoot)
|
||||
{
|
||||
// Extract archive path to destinationPath\<archiveFileName> and skipping extracting of existing files
|
||||
return AZStd::string::format(ExtractArchiveCmd, archivePath.c_str(), destinationPath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Extract archive path to destinationPath and skipping extracting of existing files
|
||||
return AZStd::string::format(ExtractArchiveWithoutRootCmd, archivePath.c_str(), destinationPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string GetAddFilesToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& listFilePath)
|
||||
{
|
||||
return AZStd::string::format(AddFilesCmd, archivePath.c_str(), listFilePath.c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetAddFileToArchiveCommand(const AZStd::string& archivePath, const AZStd::string& file)
|
||||
{
|
||||
return AZStd::string::format(AddFileCmd, archivePath.c_str(), file.c_str());
|
||||
}
|
||||
|
||||
AZStd::string GetExtractFileCommand(const AZStd::string& archivePath, const AZStd::string& fileInArchive, const AZStd::string& destinationPath, bool overWrite)
|
||||
{
|
||||
AZStd::string commandLineArgs;
|
||||
if (destinationPath.empty())
|
||||
{
|
||||
// Extract file in archive from archive path to the current directory, overwriting a file of the same name that exists there.
|
||||
commandLineArgs = AZStd::string::format(ExtractFileCmd, archivePath.c_str(), fileInArchive.c_str(), overWrite ? ExtractOverwrite : ExtractSkipExisting);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Extract file in archive from archive path to destinationPath, overwriting a file of the same name that exists there.
|
||||
commandLineArgs = AZStd::string::format(ExtractFileDestination, archivePath.c_str(), destinationPath.c_str(), fileInArchive.c_str(), overWrite ? ExtractOverwrite : ExtractSkipExisting);
|
||||
}
|
||||
|
||||
return commandLineArgs;
|
||||
}
|
||||
|
||||
AZStd::string GetListFilesInArchiveCommand(const AZStd::string& archivePath)
|
||||
{
|
||||
AZStd::string commandLineArgs = AZStd::string::format(ListFilesInArchiveCmd, archivePath.c_str());
|
||||
return commandLineArgs;
|
||||
}
|
||||
|
||||
/*
|
||||
File output for our list archive commands takes the following two patterns for files vs directories:
|
||||
|
||||
Path = basicfile2.txt
|
||||
Folder = -
|
||||
Size = 1
|
||||
Packed Size = 1
|
||||
Modified = 2019-03-26 18:31:10
|
||||
Created = 2019-03-26 18:31:10
|
||||
Accessed = 2019-03-26 18:31:10
|
||||
Attributes = A
|
||||
Encrypted = -
|
||||
Comment =
|
||||
CRC = 32D70693
|
||||
Method = Store
|
||||
Characteristics = NTFS
|
||||
Host OS = FAT
|
||||
Version = 10
|
||||
Volume Index = 0
|
||||
Offset = 44
|
||||
|
||||
Path = testfolder
|
||||
Folder = +
|
||||
Size = 0
|
||||
Packed Size = 0
|
||||
Modified = 2019-03-26 18:31:10
|
||||
Created = 2019-03-26 18:31:10
|
||||
Accessed = 2019-03-26 18:31:10
|
||||
Attributes = D
|
||||
Encrypted = -
|
||||
Comment =
|
||||
CRC =
|
||||
Method = Store
|
||||
Characteristics = NTFS
|
||||
Host OS = FAT
|
||||
Version = 20
|
||||
Volume Index = 0
|
||||
Offset = 89
|
||||
|
||||
*/
|
||||
|
||||
void ParseConsoleOutputFromListFilesInArchive(const AZStd::string& consoleOutput, AZStd::vector<AZStd::string>& fileEntries)
|
||||
{
|
||||
AZStd::vector<AZStd::string> fileEntryData;
|
||||
AzFramework::StringFunc::Tokenize(consoleOutput.c_str(), fileEntryData, "\r\n");
|
||||
for (size_t slotNum = 0; slotNum < fileEntryData.size(); ++slotNum)
|
||||
{
|
||||
AZStd::string& line = fileEntryData[slotNum];
|
||||
if (AzFramework::StringFunc::StartsWith(line, "Path = "))
|
||||
{
|
||||
if ((slotNum + 1) < fileEntryData.size())
|
||||
{
|
||||
// We're checking one past each entry we find for the Folder entry and skipping anything marked as a folder
|
||||
// See sample output above
|
||||
if (AzFramework::StringFunc::StartsWith(fileEntryData[slotNum + 1], "Folder = -"))
|
||||
{
|
||||
AzFramework::StringFunc::Replace(line, "Path = ", "", false, true);
|
||||
fileEntries.emplace_back(AZStd::move(line));
|
||||
slotNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Platform
|
||||
} // namespace AzToolsFramework
|
||||
@@ -7,5 +7,4 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
AzToolsFramework/Archive/ArchiveComponent_Windows.cpp
|
||||
)
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace UnitTest
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
bool CreateDummyFile(const QString& fullPathToFile, const QString& tempStr = {})
|
||||
{
|
||||
QFileInfo fi(fullPathToFile);
|
||||
@@ -50,7 +49,7 @@ namespace UnitTest
|
||||
return true;
|
||||
}
|
||||
|
||||
class ArchiveTest :
|
||||
class ArchiveComponentTest :
|
||||
public ::testing::Test
|
||||
{
|
||||
|
||||
@@ -73,7 +72,12 @@ namespace UnitTest
|
||||
return "Archive";
|
||||
}
|
||||
|
||||
void CreateArchiveFolder( QString archiveFolderName, QStringList fileList )
|
||||
QString GetExtractFolderName()
|
||||
{
|
||||
return "Extracted";
|
||||
}
|
||||
|
||||
void CreateArchiveFolder(QString archiveFolderName, QStringList fileList)
|
||||
{
|
||||
QDir tempPath = QDir(m_tempDir.GetDirectory()).filePath(archiveFolderName);
|
||||
|
||||
@@ -84,6 +88,14 @@ namespace UnitTest
|
||||
}
|
||||
}
|
||||
|
||||
QString CreateArchiveListTextFile()
|
||||
{
|
||||
QString listFilePath = QDir(m_tempDir.GetDirectory()).absoluteFilePath("FileList.txt");
|
||||
QString textContent = CreateArchiveFileList().join("\n");
|
||||
EXPECT_TRUE(CreateDummyFile(listFilePath, textContent));
|
||||
return listFilePath;
|
||||
}
|
||||
|
||||
void CreateArchiveFolder()
|
||||
{
|
||||
CreateArchiveFolder(GetArchiveFolderName(), CreateArchiveFileList());
|
||||
@@ -99,16 +111,24 @@ namespace UnitTest
|
||||
return QDir(m_tempDir.GetDirectory()).filePath(GetArchiveFolderName());
|
||||
}
|
||||
|
||||
QString GetExtractFolder()
|
||||
{
|
||||
return QDir(m_tempDir.GetDirectory()).filePath(GetExtractFolderName());
|
||||
}
|
||||
|
||||
bool CreateArchive()
|
||||
{
|
||||
bool createResult{ false };
|
||||
AzToolsFramework::ArchiveCommandsBus::BroadcastResult(createResult, &AzToolsFramework::ArchiveCommandsBus::Events::CreateArchiveBlocking, GetArchivePath().toStdString().c_str(), GetArchiveFolder().toStdString().c_str());
|
||||
return createResult;
|
||||
std::future<bool> createResult;
|
||||
AzToolsFramework::ArchiveCommandsBus::BroadcastResult(createResult,
|
||||
&AzToolsFramework::ArchiveCommandsBus::Events::CreateArchive,
|
||||
GetArchivePath().toUtf8().constData(), GetArchiveFolder().toUtf8().constData());
|
||||
bool result = createResult.get();
|
||||
return result;
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
m_app.reset(aznew ToolsTestApplication("ArchiveTest"));
|
||||
m_app.reset(aznew ToolsTestApplication("ArchiveComponentTest"));
|
||||
m_app->Start(AzFramework::Application::Descriptor());
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
@@ -132,76 +152,138 @@ namespace UnitTest
|
||||
};
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
TEST_F(ArchiveTest, DISABLED_CreateArchiveBlocking_FilesAtThreeDepths_ArchiveCreated)
|
||||
TEST_F(ArchiveComponentTest, DISABLED_CreateArchive_FilesAtThreeDepths_ArchiveCreated)
|
||||
#else
|
||||
TEST_F(ArchiveTest, CreateArchiveBlocking_FilesAtThreeDepths_ArchiveCreated)
|
||||
TEST_F(ArchiveComponentTest, CreateArchive_FilesAtThreeDepths_ArchiveCreated)
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
{
|
||||
EXPECT_TRUE(m_tempDir.IsValid());
|
||||
CreateArchiveFolder();
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
bool createResult = CreateArchive();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
|
||||
|
||||
EXPECT_EQ(createResult, true);
|
||||
EXPECT_TRUE(createResult);
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
TEST_F(ArchiveTest, DISABLED_ListFilesInArchiveBlocking_FilesAtThreeDepths_FilesFound)
|
||||
TEST_F(ArchiveComponentTest, DISABLED_ListFilesInArchive_FilesAtThreeDepths_FilesFound)
|
||||
#else
|
||||
TEST_F(ArchiveTest, ListFilesInArchiveBlocking_FilesAtThreeDepths_FilesFound)
|
||||
TEST_F(ArchiveComponentTest, ListFilesInArchive_FilesAtThreeDepths_FilesFound)
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
{
|
||||
EXPECT_TRUE(m_tempDir.IsValid());
|
||||
CreateArchiveFolder();
|
||||
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
EXPECT_EQ(CreateArchive(), true);
|
||||
|
||||
AZStd::vector<AZStd::string> fileList;
|
||||
bool listResult{ false };
|
||||
AzToolsFramework::ArchiveCommandsBus::BroadcastResult(listResult, &AzToolsFramework::ArchiveCommandsBus::Events::ListFilesInArchiveBlocking, GetArchivePath().toStdString().c_str(), fileList);
|
||||
AzToolsFramework::ArchiveCommandsBus::BroadcastResult(listResult,
|
||||
&AzToolsFramework::ArchiveCommandsBus::Events::ListFilesInArchive,
|
||||
GetArchivePath().toUtf8().constData(), fileList);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
|
||||
|
||||
EXPECT_TRUE(listResult);
|
||||
EXPECT_EQ(fileList.size(), 6);
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
TEST_F(ArchiveTest, DISABLED_CreateDeltaCatalog_AssetsNotRegistered_Failure)
|
||||
TEST_F(ArchiveComponentTest, DISABLED_CreateDeltaCatalog_AssetsNotRegistered_Failure)
|
||||
#else
|
||||
TEST_F(ArchiveTest, CreateDeltaCatalog_AssetsNotRegistered_Failure)
|
||||
TEST_F(ArchiveComponentTest, CreateDeltaCatalog_AssetsNotRegistered_Failure)
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
{
|
||||
QStringList fileList = CreateArchiveFileList();
|
||||
|
||||
CreateArchiveFolder(GetArchiveFolderName(), fileList);
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
bool createResult = CreateArchive();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
|
||||
|
||||
EXPECT_EQ(createResult, true);
|
||||
|
||||
bool catalogCreated{ true };
|
||||
AZ::Test::AssertAbsorber assertAbsorber;
|
||||
AzToolsFramework::AssetBundleCommandsBus::BroadcastResult(catalogCreated, &AzToolsFramework::AssetBundleCommandsBus::Events::CreateDeltaCatalog, GetArchivePath().toStdString().c_str(), true);
|
||||
AzToolsFramework::AssetBundleCommandsBus::BroadcastResult(catalogCreated,
|
||||
&AzToolsFramework::AssetBundleCommandsBus::Events::CreateDeltaCatalog, GetArchivePath().toUtf8().constData(), true);
|
||||
|
||||
EXPECT_EQ(catalogCreated, false);
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
TEST_F(ArchiveTest, DISABLED_CreateDeltaCatalog_ArchiveWithoutCatalogAssetsRegistered_Success)
|
||||
TEST_F(ArchiveComponentTest, DISABLED_AddFilesToArchive_FromListFile_Success)
|
||||
#else
|
||||
TEST_F(ArchiveTest, CreateDeltaCatalog_ArchiveWithoutCatalogAssetsRegistered_Success)
|
||||
TEST_F(ArchiveComponentTest, AddFilesToArchive_FromListFile_Success)
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
{
|
||||
QString listFile = CreateArchiveListTextFile();
|
||||
CreateArchiveFolder(GetArchiveFolderName(), CreateArchiveFileList());
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
std::future<bool> addResult;
|
||||
AzToolsFramework::ArchiveCommandsBus::BroadcastResult(
|
||||
addResult, &AzToolsFramework::ArchiveCommandsBus::Events::AddFilesToArchive, GetArchivePath().toUtf8().constData(),
|
||||
GetArchiveFolder().toUtf8().constData(), listFile.toUtf8().constData());
|
||||
bool result = addResult.get();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
TEST_F(ArchiveComponentTest, DISABLED_ExtractArchive_AllFiles_Success)
|
||||
#else
|
||||
TEST_F(ArchiveComponentTest, ExtractArchive_AllFiles_Success)
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
{
|
||||
CreateArchiveFolder();
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
bool createResult = CreateArchive();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
|
||||
EXPECT_TRUE(createResult);
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
std::future<bool> extractResult;
|
||||
AzToolsFramework::ArchiveCommandsBus::BroadcastResult(
|
||||
extractResult, &AzToolsFramework::ArchiveCommandsBus::Events::ExtractArchive, GetArchivePath().toUtf8().constData(),
|
||||
GetExtractFolder().toUtf8().constData());
|
||||
bool result = extractResult.get();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
|
||||
QStringList archiveFiles = CreateArchiveFileList();
|
||||
for (const auto& file : archiveFiles)
|
||||
{
|
||||
QString fullFilePath = QDir(GetExtractFolder()).absoluteFilePath(file);
|
||||
QFileInfo fi(fullFilePath);
|
||||
EXPECT_TRUE(fi.exists());
|
||||
}
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
TEST_F(ArchiveComponentTest, DISABLED_CreateDeltaCatalog_ArchiveWithoutCatalogAssetsRegistered_Success)
|
||||
#else
|
||||
TEST_F(ArchiveComponentTest, CreateDeltaCatalog_ArchiveWithoutCatalogAssetsRegistered_Success)
|
||||
#endif // AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS
|
||||
{
|
||||
QStringList fileList = CreateArchiveFileList();
|
||||
|
||||
CreateArchiveFolder(GetArchiveFolderName(), fileList);
|
||||
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
bool createResult = CreateArchive();
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT;
|
||||
|
||||
EXPECT_EQ(createResult, true);
|
||||
|
||||
for (const auto& thisPath : fileList)
|
||||
{
|
||||
AZ::Data::AssetInfo newInfo;
|
||||
newInfo.m_relativePath = thisPath.toStdString().c_str();
|
||||
newInfo.m_relativePath = thisPath.toUtf8().constData();
|
||||
newInfo.m_assetType = AZ::Uuid::CreateRandom();
|
||||
newInfo.m_sizeBytes = 100; // Arbitrary
|
||||
AZ::Data::AssetId generatedID(AZ::Uuid::CreateRandom());
|
||||
@@ -212,7 +294,7 @@ namespace UnitTest
|
||||
|
||||
bool catalogCreated{ false };
|
||||
AZ_TEST_START_TRACE_SUPPRESSION;
|
||||
AzToolsFramework::AssetBundleCommandsBus::BroadcastResult(catalogCreated, &AzToolsFramework::AssetBundleCommandsBus::Events::CreateDeltaCatalog, GetArchivePath().toStdString().c_str(), true);
|
||||
AzToolsFramework::AssetBundleCommandsBus::BroadcastResult(catalogCreated, &AzToolsFramework::AssetBundleCommandsBus::Events::CreateDeltaCatalog, GetArchivePath().toUtf8().constData(), true);
|
||||
AZ_TEST_STOP_TRACE_SUPPRESSION_NO_COUNT; // produces different counts in different platforms
|
||||
EXPECT_EQ(catalogCreated, true);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
AZ::Aabb BoundsTestComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
|
||||
{
|
||||
return GetWorldBounds();
|
||||
}
|
||||
|
||||
bool BoundsTestComponent::EditorSelectionIntersectRayViewport(
|
||||
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
|
||||
{
|
||||
return AzToolsFramework::AabbIntersectRay(src, dir, GetWorldBounds(), distance);
|
||||
}
|
||||
|
||||
bool BoundsTestComponent::SupportsEditorRayIntersect()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Activate()
|
||||
{
|
||||
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
|
||||
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Deactivate()
|
||||
{
|
||||
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
|
||||
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetWorldBounds()
|
||||
{
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
|
||||
return GetLocalBounds().GetTransformedAabb(worldFromLocal);
|
||||
}
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetLocalBounds()
|
||||
{
|
||||
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzFramework/Visibility/BoundsBus.h>
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
//! Basic component that implements BoundsRequestBus and EditorComponentSelectionRequestsBus to be compatible
|
||||
//! with the Editor visibility system.
|
||||
//! Note: Used for simulating selection (picking) in the viewport.
|
||||
class BoundsTestComponent
|
||||
: public AzToolsFramework::Components::EditorComponentBase
|
||||
, public AzFramework::BoundsRequestBus::Handler
|
||||
, public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_EDITOR_COMPONENT(
|
||||
BoundsTestComponent, "{E6312E9D-8489-4677-9980-C93C328BC92C}", AzToolsFramework::Components::EditorComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component overrides ...
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
// EditorComponentSelectionRequestsBus overrides ...
|
||||
AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
|
||||
bool EditorSelectionIntersectRayViewport(
|
||||
const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override;
|
||||
bool SupportsEditorRayIntersect() override;
|
||||
|
||||
// BoundsRequestBus overrides ...
|
||||
AZ::Aabb GetWorldBounds() override;
|
||||
AZ::Aabb GetLocalBounds() override;
|
||||
};
|
||||
|
||||
} // namespace UnitTest
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <AzFramework/Components/TransformComponent.h>
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
#include <AzFramework/Visibility/BoundsBus.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
|
||||
@@ -22,12 +21,10 @@
|
||||
#include <AzManipulatorTestFramework/ViewportInteraction.h>
|
||||
#include <AzQtComponents/Components/GlobalEventFilter.h>
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <AzToolsFramework/Application/ToolsApplication.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityModel.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
@@ -41,6 +38,8 @@
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
#include <AzToolsFramework/ViewportUi/ViewportUiManager.h>
|
||||
|
||||
#include<Tests/BoundsTestComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
std::ostream& operator<<(std::ostream& os, const EntityId entityId)
|
||||
@@ -123,80 +122,6 @@ namespace UnitTest
|
||||
EXPECT_FALSE(m_cache.IsVisibleEntityVisible(m_cache.GetVisibleEntityIndexFromId(m_entityIds[2]).value()));
|
||||
}
|
||||
|
||||
//! Basic component that implements BoundsRequestBus and EditorComponentSelectionRequestsBus to be compatible
|
||||
//! with the Editor visibility system.
|
||||
//! Note: Used for simulating selection (picking) in the viewport.
|
||||
class BoundsTestComponent
|
||||
: public AzToolsFramework::Components::EditorComponentBase
|
||||
, public AzFramework::BoundsRequestBus::Handler
|
||||
, public AzToolsFramework::EditorComponentSelectionRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_EDITOR_COMPONENT(
|
||||
BoundsTestComponent, "{E6312E9D-8489-4677-9980-C93C328BC92C}", AzToolsFramework::Components::EditorComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component overrides ...
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
// EditorComponentSelectionRequestsBus overrides ...
|
||||
AZ::Aabb GetEditorSelectionBoundsViewport(const AzFramework::ViewportInfo& viewportInfo) override;
|
||||
bool EditorSelectionIntersectRayViewport(
|
||||
const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance) override;
|
||||
bool SupportsEditorRayIntersect() override;
|
||||
|
||||
// BoundsRequestBus overrides ...
|
||||
AZ::Aabb GetWorldBounds() override;
|
||||
AZ::Aabb GetLocalBounds() override;
|
||||
};
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
|
||||
{
|
||||
return GetWorldBounds();
|
||||
}
|
||||
|
||||
bool BoundsTestComponent::EditorSelectionIntersectRayViewport(
|
||||
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
|
||||
{
|
||||
return AzToolsFramework::AabbIntersectRay(src, dir, GetWorldBounds(), distance);
|
||||
}
|
||||
|
||||
bool BoundsTestComponent::SupportsEditorRayIntersect()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Activate()
|
||||
{
|
||||
AzFramework::BoundsRequestBus::Handler::BusConnect(GetEntityId());
|
||||
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
|
||||
}
|
||||
|
||||
void BoundsTestComponent::Deactivate()
|
||||
{
|
||||
AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
|
||||
AzFramework::BoundsRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetWorldBounds()
|
||||
{
|
||||
AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::EventResult(worldFromLocal, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
|
||||
return GetLocalBounds().GetTransformedAabb(worldFromLocal);
|
||||
}
|
||||
|
||||
AZ::Aabb BoundsTestComponent::GetLocalBounds()
|
||||
{
|
||||
return AZ::Aabb::CreateFromMinMax(AZ::Vector3(-0.5f), AZ::Vector3(0.5f));
|
||||
}
|
||||
|
||||
// Fixture to support testing EditorTransformComponentSelection functionality on an Entity selection.
|
||||
class EditorTransformComponentSelectionFixture : public ToolsApplicationFixture
|
||||
{
|
||||
@@ -344,9 +269,10 @@ namespace UnitTest
|
||||
using AzToolsFramework::EditorInteractionSystemViewportSelectionRequestBus;
|
||||
EditorInteractionSystemViewportSelectionRequestBus::Event(
|
||||
AzToolsFramework::GetEntityContextId(), &EditorInteractionSystemViewportSelectionRequestBus::Events::SetHandler,
|
||||
[](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache)
|
||||
[](const AzToolsFramework::EditorVisibleEntityDataCache* entityDataCache,
|
||||
[[maybe_unused]] AzToolsFramework::ViewportEditorModeTrackerInterface* viewportEditorModeTracker)
|
||||
{
|
||||
return AZStd::make_unique<AzToolsFramework::EditorPickEntitySelection>(entityDataCache);
|
||||
return AZStd::make_unique<AzToolsFramework::EditorPickEntitySelection>(entityDataCache, viewportEditorModeTracker);
|
||||
});
|
||||
|
||||
// When
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
|
||||
#include <Tests/BoundsTestComponent.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
void EditorFocusModeFixture::SetUpEditorFixtureImpl()
|
||||
{
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
// in the unit tests.
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
|
||||
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
ASSERT_TRUE(m_focusModeInterface != nullptr);
|
||||
|
||||
// register a simple component implementing BoundsRequestBus and EditorComponentSelectionRequestsBus
|
||||
GetApplication()->RegisterComponentDescriptor(UnitTest::BoundsTestComponent::CreateDescriptor());
|
||||
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
|
||||
GenerateTestHierarchy();
|
||||
}
|
||||
|
||||
void EditorFocusModeFixture::GenerateTestHierarchy()
|
||||
{
|
||||
/*
|
||||
* City
|
||||
* |_ Street
|
||||
* |_ Car
|
||||
* | |_ Passenger
|
||||
* |_ SportsCar
|
||||
* |_ Passenger
|
||||
*/
|
||||
|
||||
m_entityMap[CityEntityName] = CreateEditorEntity(CityEntityName, AZ::EntityId());
|
||||
m_entityMap[StreetEntityName] = CreateEditorEntity(StreetEntityName, m_entityMap[CityEntityName]);
|
||||
m_entityMap[CarEntityName] = CreateEditorEntity(CarEntityName, m_entityMap[StreetEntityName]);
|
||||
m_entityMap[Passenger1EntityName] = CreateEditorEntity(Passenger1EntityName, m_entityMap[CarEntityName]);
|
||||
m_entityMap[SportsCarEntityName] = CreateEditorEntity(SportsCarEntityName, m_entityMap[StreetEntityName]);
|
||||
m_entityMap[Passenger2EntityName] = CreateEditorEntity(Passenger2EntityName, m_entityMap[SportsCarEntityName]);
|
||||
|
||||
// Add a BoundsTestComponent to the Car entity.
|
||||
AZ::Entity* entity = GetEntityById(m_entityMap[CarEntityName]);
|
||||
|
||||
entity->Deactivate();
|
||||
entity->CreateComponent<UnitTest::BoundsTestComponent>();
|
||||
entity->Activate();
|
||||
|
||||
// Move the CarEntity so it's out of the way.
|
||||
AZ::TransformBus::Event(m_entityMap[CarEntityName], &AZ::TransformBus::Events::SetWorldTranslation, CarEntityPosition);
|
||||
|
||||
// Setup the camera so the Car entity is in view.
|
||||
AzFramework::SetCameraTransform(
|
||||
m_cameraState,
|
||||
AZ::Transform::CreateFromQuaternionAndTranslation(
|
||||
AZ::Quaternion::CreateFromEulerAnglesDegrees(AZ::Vector3(0.0f, 0.0f, 0.0f)), CameraPosition));
|
||||
}
|
||||
|
||||
AZ::EntityId EditorFocusModeFixture::CreateEditorEntity(const char* name, AZ::EntityId parentId)
|
||||
{
|
||||
AZ::Entity* entity = nullptr;
|
||||
UnitTest::CreateDefaultEditorEntity(name, &entity);
|
||||
|
||||
// Parent
|
||||
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId);
|
||||
|
||||
return entity->GetId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EditorFocusModeFixture
|
||||
: public UnitTest::ToolsApplicationFixture
|
||||
{
|
||||
protected:
|
||||
void SetUpEditorFixtureImpl() override;
|
||||
|
||||
void GenerateTestHierarchy();
|
||||
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId);
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
|
||||
FocusModeInterface* m_focusModeInterface = nullptr;
|
||||
|
||||
public:
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
|
||||
AzFramework::CameraState m_cameraState;
|
||||
|
||||
inline static const AZ::Vector3 CameraPosition = AZ::Vector3(10.0f, 15.0f, 10.0f);
|
||||
|
||||
inline static const char* CityEntityName = "City";
|
||||
inline static const char* StreetEntityName = "Street";
|
||||
inline static const char* CarEntityName = "Car";
|
||||
inline static const char* SportsCarEntityName = "SportsCar";
|
||||
inline static const char* Passenger1EntityName = "Passenger1";
|
||||
inline static const char* Passenger2EntityName = "Passenger2";
|
||||
|
||||
inline static AZ::Vector3 CarEntityPosition = AZ::Vector3(5.0f, 15.0f, 0.0f);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
#include <AzFramework/Viewport/ViewportScreen.h>
|
||||
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
|
||||
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkTestHelpers.h>
|
||||
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
|
||||
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
|
||||
#include <AzManipulatorTestFramework/IndirectManipulatorViewportInteraction.h>
|
||||
|
||||
#include <AzToolsFramework/Component/EditorComponentAPIBus.h>
|
||||
#include <AzToolsFramework/Manipulators/LinearManipulator.h>
|
||||
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorVisibleEntityDataCache.h>
|
||||
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EditorFocusModeSelectionFixture
|
||||
: public UnitTest::IndirectCallManipulatorViewportInteractionFixtureMixin<EditorFocusModeFixture>
|
||||
{
|
||||
public:
|
||||
void ClickAtWorldPositionOnViewport(const AZ::Vector3& worldPosition)
|
||||
{
|
||||
// Calculate the world position in screen space
|
||||
const auto carScreenPosition = AzFramework::WorldToScreen(worldPosition, m_cameraState);
|
||||
|
||||
// Click the entity in the viewport
|
||||
m_actionDispatcher->CameraState(m_cameraState)->MousePosition(carScreenPosition)->MouseLButtonDown()->MouseLButtonUp();
|
||||
}
|
||||
};
|
||||
|
||||
void ClearSelectedEntities()
|
||||
{
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequestBus::Events::SetSelectedEntities, AzToolsFramework::EntityIdList());
|
||||
}
|
||||
|
||||
AzToolsFramework::EntityIdList GetSelectedEntities()
|
||||
{
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
|
||||
selectedEntities, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetSelectedEntities);
|
||||
return selectedEntities;
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnLevel)
|
||||
{
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnAncestor)
|
||||
{
|
||||
// Set the focus on the Street Entity (parent of the test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnItself)
|
||||
{
|
||||
// Set the focus on the Car Entity (test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 1);
|
||||
EXPECT_EQ(selectedEntitiesAfter.front(), m_entityMap[CarEntityName]);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnSibling)
|
||||
{
|
||||
// Set the focus on the SportsCar Entity (sibling of the test entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[SportsCarEntityName]);
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeSelectionFixture, EditorFocusModeSelectionTests_SelectEntityWithFocusOnDescendant)
|
||||
{
|
||||
// Set the focus on the Passenger1 Entity (child of the entity)
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger1EntityName]);
|
||||
// Clear selection
|
||||
ClearSelectedEntities();
|
||||
|
||||
// Click on Car Entity
|
||||
ClickAtWorldPositionOnViewport(CarEntityPosition);
|
||||
|
||||
// Verify entity is selected
|
||||
auto selectedEntitiesAfter = GetSelectedEntities();
|
||||
EXPECT_EQ(selectedEntitiesAfter.size(), 0);
|
||||
|
||||
// Clear the focus, disabling focus mode
|
||||
m_focusModeInterface->ClearFocusRoot(AzFramework::EntityContextId::CreateNull());
|
||||
}
|
||||
}
|
||||
@@ -6,123 +6,99 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzTest/AzTest.h>
|
||||
#include <AzCore/UserSettings/UserSettingsComponent.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <Tests/FocusMode/EditorFocusModeFixture.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class EditorFocusModeTests
|
||||
: public ::testing::Test
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_SetFocus)
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
m_app.Start(m_descriptor);
|
||||
// When an entity is set as the focus root, GetFocusRoot should return its EntityId.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
EXPECT_EQ(m_focusModeInterface->GetFocusRoot(m_editorEntityContextId), m_entityMap[CarEntityName]);
|
||||
|
||||
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
|
||||
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
|
||||
// in the unit tests.
|
||||
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
|
||||
|
||||
GenerateTestHierarchy();
|
||||
}
|
||||
|
||||
void GenerateTestHierarchy()
|
||||
{
|
||||
/*
|
||||
* City
|
||||
* |_ Street
|
||||
* |_ Car
|
||||
* | |_ Passenger
|
||||
* |_ SportsCar
|
||||
* |_ Passenger
|
||||
*/
|
||||
|
||||
m_entityMap["cityId"] = CreateEditorEntity("City", AZ::EntityId());
|
||||
m_entityMap["streetId"] = CreateEditorEntity("Street", m_entityMap["cityId"]);
|
||||
m_entityMap["carId"] = CreateEditorEntity("Car", m_entityMap["streetId"]);
|
||||
m_entityMap["passengerId1"] = CreateEditorEntity("Passenger", m_entityMap["carId"]);
|
||||
m_entityMap["sportsCarId"] = CreateEditorEntity("SportsCar", m_entityMap["streetId"]);
|
||||
m_entityMap["passengerId2"] = CreateEditorEntity("Passenger", m_entityMap["sportsCarId"]);
|
||||
}
|
||||
|
||||
AZ::EntityId CreateEditorEntity(const char* name, AZ::EntityId parentId)
|
||||
{
|
||||
AZ::Entity* entity = nullptr;
|
||||
UnitTest::CreateDefaultEditorEntity(name, &entity);
|
||||
|
||||
// Parent
|
||||
AZ::TransformBus::Event(entity->GetId(), &AZ::TransformInterface::SetParent, parentId);
|
||||
|
||||
return entity->GetId();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_app.Stop();
|
||||
}
|
||||
|
||||
UnitTest::ToolsTestApplication m_app{ "EditorFocusModeTests" };
|
||||
AZ::ComponentApplication::Descriptor m_descriptor;
|
||||
AZStd::unordered_map<AZStd::string, AZ::EntityId> m_entityMap;
|
||||
};
|
||||
|
||||
TEST_F(EditorFocusModeTests, EditorFocusModeTests_SetFocus)
|
||||
{
|
||||
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
EXPECT_TRUE(focusModeInterface != nullptr);
|
||||
|
||||
focusModeInterface->SetFocusRoot(m_entityMap["carId"]);
|
||||
EXPECT_EQ(focusModeInterface->GetFocusRoot(), m_entityMap["carId"]);
|
||||
|
||||
focusModeInterface->ClearFocusRoot();
|
||||
EXPECT_EQ(focusModeInterface->GetFocusRoot(), AZ::EntityId());
|
||||
// Restore default expected focus.
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeTests, EditorFocusModeTests_IsInFocusSubTree)
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_ClearFocus)
|
||||
{
|
||||
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
|
||||
EXPECT_TRUE(focusModeInterface != nullptr);
|
||||
// Change the value from the default.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
|
||||
focusModeInterface->ClearFocusRoot();
|
||||
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
|
||||
// Calling ClearFocusRoot restores the default focus root (which is an invalid EntityId).
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
EXPECT_EQ(m_focusModeInterface->GetFocusRoot(m_editorEntityContextId), AZ::EntityId());
|
||||
}
|
||||
|
||||
focusModeInterface->SetFocusRoot(m_entityMap["streetId"]);
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_AncestorsDescendants)
|
||||
{
|
||||
// When the focus is set to an entity, all its descendants are in the focus subtree while the ancestors aren't.
|
||||
{
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
|
||||
focusModeInterface->SetFocusRoot(m_entityMap["carId"]);
|
||||
// Restore default expected focus.
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
}
|
||||
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), true);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), false);
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Siblings)
|
||||
{
|
||||
// If the root entity has siblings, they are also outside of the focus subtree.
|
||||
{
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[CarEntityName]);
|
||||
|
||||
focusModeInterface->SetFocusRoot(m_entityMap["passengerId2"]);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), false);
|
||||
}
|
||||
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["cityId"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["streetId"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["carId"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId1"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["sportsCarId"]), false);
|
||||
EXPECT_EQ(focusModeInterface->IsInFocusSubTree(m_entityMap["passengerId2"]), true);
|
||||
// Restore default expected focus.
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
}
|
||||
|
||||
focusModeInterface->ClearFocusRoot();
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Leaf)
|
||||
{
|
||||
// If the root is a leaf, then the focus subtree will consists of just that entity.
|
||||
{
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[Passenger2EntityName]);
|
||||
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), false);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
|
||||
// Restore default expected focus.
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
}
|
||||
|
||||
TEST_F(EditorFocusModeFixture, EditorFocusModeTests_IsInFocusSubTree_Clear)
|
||||
{
|
||||
// Change the value from the default.
|
||||
m_focusModeInterface->SetFocusRoot(m_entityMap[StreetEntityName]);
|
||||
|
||||
// When the focus is cleared, the whole level is in the focus subtree; so we expect all entities to return true.
|
||||
{
|
||||
m_focusModeInterface->ClearFocusRoot(m_editorEntityContextId);
|
||||
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CityEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[StreetEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[CarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger1EntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[SportsCarEntityName]), true);
|
||||
EXPECT_EQ(m_focusModeInterface->IsInFocusSubTree(m_entityMap[Passenger2EntityName]), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Benchmark
|
||||
AZStd::unique_ptr<Instance> instance = m_prefabSystemComponent->CreatePrefab(
|
||||
entities
|
||||
, {}
|
||||
, m_pathString);
|
||||
, m_pathString);
|
||||
|
||||
state.PauseTiming();
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace Benchmark
|
||||
{
|
||||
nestedInstanceRoot = m_prefabSystemComponent->CreatePrefab(
|
||||
{},
|
||||
MakeInstanceList( AZStd::move(nestedInstanceRoot) ),
|
||||
MakeInstanceList(AZStd::move(nestedInstanceRoot)),
|
||||
m_paths[instanceCounter]);
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -36,7 +36,7 @@ namespace Benchmark
|
||||
|
||||
AZStd::unique_ptr<Instance> enclosingInstance = m_prefabSystemComponent->CreatePrefab(
|
||||
{},
|
||||
MakeInstanceList( AZStd::move(nestedInstance) ),
|
||||
MakeInstanceList(AZStd::move(nestedInstance)),
|
||||
enclosingTemplatePath);
|
||||
|
||||
TemplateId templateToInstantiateId = enclosingInstance->GetTemplateId();
|
||||
@@ -99,7 +99,7 @@ namespace Benchmark
|
||||
{
|
||||
currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
|
||||
{},
|
||||
MakeInstanceList( AZStd::move(currentInstanceRoot) ),
|
||||
MakeInstanceList(AZStd::move(currentInstanceRoot)),
|
||||
m_paths[currentDepth - 1]);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace Benchmark
|
||||
{
|
||||
currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
|
||||
{},
|
||||
MakeInstanceList( AZStd::move(currentInstanceRoot) ),
|
||||
MakeInstanceList(AZStd::move(currentInstanceRoot)),
|
||||
m_paths[currentDepth]);
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ namespace Benchmark
|
||||
|
||||
currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
|
||||
{},
|
||||
MakeInstanceList( AZStd::move(currentInstanceRoot), AZStd::move(extraNestedInstance) ),
|
||||
MakeInstanceList(AZStd::move(currentInstanceRoot), AZStd::move(extraNestedInstance)),
|
||||
m_paths[currentDepth]);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,88 +30,137 @@ namespace UnitTest
|
||||
* |_ Passenger
|
||||
*/
|
||||
|
||||
m_entityMap["passenger1"] = CreateEntity("Passenger1");
|
||||
m_entityMap["passenger2"] = CreateEntity("Passenger2");
|
||||
m_entityMap["city"] = CreateEntity("City");
|
||||
// Create loose entities
|
||||
m_entityMap[Passenger1EntityName] = CreateEntity(Passenger1EntityName);
|
||||
m_entityMap[Passenger2EntityName] = CreateEntity(Passenger2EntityName);
|
||||
m_entityMap[CityEntityName] = CreateEntity(CityEntityName);
|
||||
|
||||
// Call HandleEntitiesAdded to the loose entities to register them with the Prefab EOS
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
|
||||
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded,
|
||||
AzToolsFramework::EntityList{ m_entityMap["passenger1"], m_entityMap["passenger2"], m_entityMap["city"] });
|
||||
AzToolsFramework::EntityList{ m_entityMap[Passenger1EntityName], m_entityMap[Passenger2EntityName], m_entityMap[CityEntityName] });
|
||||
|
||||
// Create a car prefab from the passenger1 entity. The container entity will be created as part of the process.
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> carInstance =
|
||||
m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger1"] }, {}, "test/car");
|
||||
m_prefabSystemComponent->CreatePrefab({ m_entityMap[Passenger1EntityName] }, {}, "test/car");
|
||||
ASSERT_TRUE(carInstance);
|
||||
m_instanceMap["car"] = carInstance.get();
|
||||
m_instanceMap[CarEntityName] = carInstance.get();
|
||||
|
||||
// Create a sportscar prefab from the passenger2 entity. The container entity will be created as part of the process.
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sportsCarInstance =
|
||||
m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger2"] }, {}, "test/sportsCar");
|
||||
m_prefabSystemComponent->CreatePrefab({ m_entityMap[Passenger2EntityName] }, {}, "test/sportsCar");
|
||||
ASSERT_TRUE(sportsCarInstance);
|
||||
m_instanceMap["sportsCar"] = sportsCarInstance.get();
|
||||
m_instanceMap[SportsCarEntityName] = sportsCarInstance.get();
|
||||
|
||||
// Create a street prefab that nests the car and sportscar instances created above. The container entity will be created as part of the process.
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> streetInstance =
|
||||
m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(carInstance), AZStd::move(sportsCarInstance) ), "test/street");
|
||||
m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList(AZStd::move(carInstance), AZStd::move(sportsCarInstance)), "test/street");
|
||||
ASSERT_TRUE(streetInstance);
|
||||
m_instanceMap["street"] = streetInstance.get();
|
||||
m_instanceMap[StreetEntityName] = streetInstance.get();
|
||||
|
||||
// Create a city prefab that nests the street instances created above and the city entity. The container entity will be created as part of the process.
|
||||
m_rootInstance =
|
||||
m_prefabSystemComponent->CreatePrefab({ m_entityMap["city"] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city");
|
||||
m_prefabSystemComponent->CreatePrefab({ m_entityMap[CityEntityName] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city");
|
||||
ASSERT_TRUE(m_rootInstance);
|
||||
m_instanceMap["city"] = m_rootInstance.get();
|
||||
m_instanceMap[CityEntityName] = m_rootInstance.get();
|
||||
}
|
||||
|
||||
void SetUpEditorFixtureImpl() override
|
||||
{
|
||||
PrefabTestFixture::SetUpEditorFixtureImpl();
|
||||
|
||||
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
ASSERT_TRUE(m_prefabFocusInterface != nullptr);
|
||||
|
||||
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
|
||||
m_editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
|
||||
|
||||
GenerateTestHierarchy();
|
||||
}
|
||||
|
||||
void TearDownEditorFixtureImpl() override
|
||||
{
|
||||
m_rootInstance.release();
|
||||
|
||||
PrefabTestFixture::TearDownEditorFixtureImpl();
|
||||
}
|
||||
|
||||
AZStd::unordered_map<AZStd::string, AZ::Entity*> m_entityMap;
|
||||
AZStd::unordered_map<AZStd::string, Instance*> m_instanceMap;
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> m_rootInstance;
|
||||
|
||||
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
|
||||
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
|
||||
inline static const char* CityEntityName = "City";
|
||||
inline static const char* StreetEntityName = "Street";
|
||||
inline static const char* CarEntityName = "Car";
|
||||
inline static const char* SportsCarEntityName = "SportsCar";
|
||||
inline static const char* Passenger1EntityName = "Passenger1";
|
||||
inline static const char* Passenger2EntityName = "Passenger2";
|
||||
};
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab)
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootContainer)
|
||||
{
|
||||
GenerateTestHierarchy();
|
||||
|
||||
PrefabFocusInterface* prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
EXPECT_TRUE(prefabFocusInterface != nullptr);
|
||||
|
||||
// Verify FocusOnOwningPrefab works when passing the container entity of the root prefab.
|
||||
{
|
||||
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId());
|
||||
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId());
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId),
|
||||
m_instanceMap[CityEntityName]->GetTemplateId());
|
||||
|
||||
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
|
||||
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId);
|
||||
EXPECT_TRUE(instance.has_value());
|
||||
EXPECT_EQ(&instance->get(), m_instanceMap["city"]);
|
||||
EXPECT_EQ(&instance->get(), m_instanceMap[CityEntityName]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_RootEntity)
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing a nested entity of the root prefab.
|
||||
{
|
||||
prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["city"]->GetId());
|
||||
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["city"]->GetTemplateId());
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[CityEntityName]->GetId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId),
|
||||
m_instanceMap[CityEntityName]->GetTemplateId());
|
||||
|
||||
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
|
||||
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId);
|
||||
EXPECT_TRUE(instance.has_value());
|
||||
EXPECT_EQ(&instance->get(), m_instanceMap["city"]);
|
||||
EXPECT_EQ(&instance->get(), m_instanceMap[CityEntityName]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_NestedContainer)
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing the container entity of a nested prefab.
|
||||
{
|
||||
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["car"]->GetContainerEntityId());
|
||||
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId());
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CarEntityName]->GetContainerEntityId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId());
|
||||
|
||||
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
|
||||
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId);
|
||||
EXPECT_TRUE(instance.has_value());
|
||||
EXPECT_EQ(&instance->get(), m_instanceMap["car"]);
|
||||
EXPECT_EQ(&instance->get(), m_instanceMap[CarEntityName]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_NestedEntity)
|
||||
{
|
||||
// Verify FocusOnOwningPrefab works when passing a nested entity of the a nested prefab.
|
||||
{
|
||||
prefabFocusInterface->FocusOnOwningPrefab(m_entityMap["passenger1"]->GetId());
|
||||
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), m_instanceMap["car"]->GetTemplateId());
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_entityMap[Passenger1EntityName]->GetId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), m_instanceMap[CarEntityName]->GetTemplateId());
|
||||
|
||||
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
|
||||
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId);
|
||||
EXPECT_TRUE(instance.has_value());
|
||||
EXPECT_EQ(&instance->get(), m_instanceMap["car"]);
|
||||
EXPECT_EQ(&instance->get(), m_instanceMap[CarEntityName]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab_Clear)
|
||||
{
|
||||
// Verify FocusOnOwningPrefab points to the root prefab when the focus is cleared.
|
||||
{
|
||||
AzToolsFramework::PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
|
||||
@@ -120,54 +169,52 @@ namespace UnitTest
|
||||
prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
|
||||
EXPECT_TRUE(rootPrefabInstance.has_value());
|
||||
|
||||
prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), rootPrefabInstance->get().GetTemplateId());
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
|
||||
EXPECT_EQ(
|
||||
m_prefabFocusInterface->GetFocusedPrefabTemplateId(m_editorEntityContextId), rootPrefabInstance->get().GetTemplateId());
|
||||
|
||||
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
|
||||
auto instance = m_prefabFocusInterface->GetFocusedPrefabInstance(m_editorEntityContextId);
|
||||
EXPECT_TRUE(instance.has_value());
|
||||
EXPECT_EQ(&instance->get(), &rootPrefabInstance->get());
|
||||
}
|
||||
|
||||
m_rootInstance.release();
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused)
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_Content)
|
||||
{
|
||||
GenerateTestHierarchy();
|
||||
|
||||
PrefabFocusInterface* prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
|
||||
EXPECT_TRUE(prefabFocusInterface != nullptr);
|
||||
|
||||
// Verify IsOwningPrefabBeingFocused returns true for all entities in a focused prefab (container/nested)
|
||||
{
|
||||
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["city"]->GetContainerEntityId());
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[CityEntityName]->GetContainerEntityId());
|
||||
|
||||
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId()));
|
||||
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId()));
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId()));
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_AncestorsDescendants)
|
||||
{
|
||||
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants)
|
||||
{
|
||||
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["street"]->GetContainerEntityId());
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[StreetEntityName]->GetContainerEntityId());
|
||||
|
||||
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["street"]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId()));
|
||||
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId()));
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[StreetEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CityEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[CityEntityName]->GetId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused_Siblings)
|
||||
{
|
||||
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings)
|
||||
{
|
||||
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["sportsCar"]->GetContainerEntityId());
|
||||
m_prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap[SportsCarEntityName]->GetContainerEntityId());
|
||||
|
||||
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["sportsCar"]->GetContainerEntityId()));
|
||||
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger2"]->GetId()));
|
||||
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["car"]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["passenger1"]->GetId()));
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[SportsCarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_TRUE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger2EntityName]->GetId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap[CarEntityName]->GetContainerEntityId()));
|
||||
EXPECT_FALSE(m_prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap[Passenger1EntityName]->GetId()));
|
||||
}
|
||||
|
||||
m_rootInstance.release();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -320,7 +320,7 @@ namespace UnitTest
|
||||
Instance& addedInstance = *addedInstancePtr;
|
||||
|
||||
//create a first instance where the instance will be removed
|
||||
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(addedInstancePtr) ), "test/path");
|
||||
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList(AZStd::move(addedInstancePtr)), "test/path");
|
||||
ASSERT_TRUE(firstInstance);
|
||||
|
||||
//get added instance alias
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user