Merging latest development

Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
kberg-amzn
2021-09-27 15:50:22 -07:00
482 changed files with 10637 additions and 2735 deletions
@@ -207,12 +207,6 @@ namespace AZ
ActivateComponent(**it);
}
// Cache the transform interface to the transform interface
// Generally this pattern is not recommended unless for component event buses
// As we have a guarantee (by design) that components can't change during active state)
// Even though technically they can connect disconnect from the bus.
m_transform = TransformBus::FindFirstHandler(m_id);
SetState(State::Active);
EBUS_EVENT_ID(m_id, EntityBus, OnEntityActivated, m_id);
@@ -1320,6 +1314,19 @@ namespace AZ
return *processSignature;
}
AZ::TransformInterface* Entity::GetTransform() const
{
// Lazy evaluation of the cached entity transform.
if(!m_transform)
{
// Generally this pattern is not recommended unless for component event buses
// As we have a guarantee (by design) that components can't change during active state)
// Even though technically they can connect disconnect from the bus.
m_transform = TransformBus::FindFirstHandler(m_id);
}
return m_transform;
}
//=========================================================================
// MakeId
// Ids must be unique across a project at authoring time. Runtime doesn't matter
@@ -354,10 +354,9 @@ namespace AZ
//! @return The Process Signature of the local machine.
static AZ::u32 GetProcessSignature();
/// @cond EXCLUDE_DOCS
//! @deprecated Use the TransformBus to communicate with the TransformInterface.
inline TransformInterface* GetTransform() const { return m_transform; }
/// @endcond
//! Gets the TransformInterface for the entity.
//! @return The TransformInterface for the entity.
TransformInterface* GetTransform() const;
//! Sorts an entity's components based on the dependencies between components.
//! If all dependencies are met, the required services can be activated
@@ -406,7 +405,7 @@ namespace AZ
//! A cached pointer to the transform interface.
//! We recommend using AZ::TransformBus and caching locally instead of accessing
//! the transform interface directly through this pointer.
TransformInterface* m_transform;
mutable TransformInterface* m_transform;
//! A user-friendly name for the entity. This makes error messages easier to read.
AZStd::string m_name;
@@ -0,0 +1,200 @@
/*
* 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/IO/FileReader.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
namespace AZ::IO
{
FileReader::FileReader() = default;
FileReader::FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
{
Open(fileIoBase, filePath);
}
FileReader::~FileReader()
{
Close();
}
FileReader::FileReader(FileReader&& other)
{
AZStd::swap(m_file, other.m_file);
AZStd::swap(m_fileIoBase, other.m_fileIoBase);
}
FileReader& FileReader::operator=(FileReader&& other)
{
// Close the current file and take over other file
Close();
m_file = AZStd::move(other.m_file);
m_fileIoBase = AZStd::move(other.m_fileIoBase);
other.m_file = AZStd::monostate{};
other.m_fileIoBase = {};
return *this;
}
bool FileReader::Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath)
{
// Close file if the FileReader has an instance open
Close();
if (fileIoBase != nullptr)
{
AZ::IO::HandleType fileHandle;
if (fileIoBase->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
{
m_file = fileHandle;
m_fileIoBase = fileIoBase;
return true;
}
}
else
{
AZ::IO::SystemFile file;
if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
{
m_file = AZStd::move(file);
return true;
}
}
return false;
}
bool FileReader::IsOpen() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return *fileHandle != AZ::IO::InvalidHandle;
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->IsOpen();
}
return false;
}
void FileReader::Close()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (AZ::IO::FileIOBase* fileIo = m_fileIoBase; fileIo != nullptr)
{
fileIo->Close(*fileHandle);
}
}
m_file = AZStd::monostate{};
m_fileIoBase = {};
}
auto FileReader::Length() const -> SizeType
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType fileSize{}; m_fileIoBase->Size(*fileHandle, fileSize))
{
return fileSize;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Length();
}
return 0;
}
auto FileReader::Read(SizeType byteSize, void* buffer) -> SizeType
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType bytesRead{}; m_fileIoBase->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
{
return bytesRead;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Read(byteSize, buffer);
}
return 0;
}
auto FileReader::Tell() const -> SizeType
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (SizeType fileOffset{}; m_fileIoBase->Tell(*fileHandle, fileOffset))
{
return fileOffset;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Tell();
}
return 0;
}
bool FileReader::Seek(AZ::s64 offset, SeekType type)
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return m_fileIoBase->Seek(*fileHandle, offset, type);
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
systemFile->Seek(offset, static_cast<AZ::IO::SystemFile::SeekMode>(type));
return true;
}
return false;
}
bool FileReader::Eof() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return m_fileIoBase->Eof(*fileHandle);
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Eof();
}
return false;
}
bool FileReader::GetFilePath(AZ::IO::FixedMaxPath& filePath) const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
AZ::IO::FixedMaxPathString& pathStringRef = filePath.Native();
if (m_fileIoBase->GetFilename(*fileHandle, pathStringRef.data(), pathStringRef.capacity()))
{
pathStringRef.resize_no_construct(AZStd::char_traits<char>::length(pathStringRef.data()));
return true;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
filePath = systemFile->Name();
return true;
}
return false;
}
}
@@ -0,0 +1,92 @@
/*
* 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/IO/Path/Path_fwd.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/containers/variant.h>
namespace AZ::IO
{
class FileIOBase;
enum class SeekType : AZ::u32;
//! Structure which encapsulates delegates File Read operations
//! to either the FileIOBase or SystemFile classes based if a FileIOBase* instance has been supplied
//! to the FileSystemReader class
//! the SettingsRegistry option to use FileIO
class FileReader
{
using HandleType = AZ::u32;
using FileHandleType = AZStd::variant<AZStd::monostate, AZ::IO::SystemFile, HandleType>;
public:
using SizeType = AZ::u64;
//! Creates FileReader instance in the default state with no file opend
FileReader();
~FileReader();
//! Creates a new FileReader instance and attempts to open the file at the supplied path
//! Uses the FileIOBase instance if supplied
//! @param fileIOBase pointer to fileIOBase instance
//! @param null-terminated filePath to open
FileReader(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
//! Takes ownership of the supplied FileReader handle
FileReader(FileReader&& other);
//! Moves ownership of FileReader handle to this instance
FileReader& operator=(FileReader&& other);
//! Opens a File using the FileIOBase instance if non-nullptr
//! Otherwise fall back to use SystemFile
//! @param fileIOBase pointer to fileIOBase instance
//! @param null-terminated filePath to open
//! @return true if the File is opened successfully
bool Open(AZ::IO::FileIOBase* fileIoBase, const char* filePath);
//! Returns true if a file is currently open
//! @return true if the file is open
bool IsOpen() const;
//! Closes the File
void Close();
//! Retrieve the length of the OpenFile
SizeType Length() const;
//! Attempts to read up to byte size bytes into the supplied buffer
//! @param byteSize - Maximum number of bytes to read
//! @param buffer - Buffer to read bytes into
//! @returns the number of bytes read if the file is open, otherwise 0
SizeType Read(SizeType byteSize, void* buffer);
//! Returns the current file offset
//! @returns file offset if the file is open, otherwise 0
SizeType Tell() const;
//! Seeks within the open file to the offset supplied
//! @param offset File offset to seek to
//! @param type parameter to indicate the reference point to start the seek from
//! @returns true if the file is open and the seek succeeded
bool Seek(AZ::s64 offset, SeekType type);
//! Returns true if the file is open and in the EOF state
bool Eof() const;
//! Store the file path of the open file into the output file path parameter
//! The filePath reference is left unmodified, if the path was not stored
//! @return true if the filePath was stored
bool GetFilePath(AZ::IO::FixedMaxPath& filePath) const;
private:
FileHandleType m_file;
AZ::IO::FileIOBase* m_fileIoBase{};
};
}
@@ -160,12 +160,12 @@ void SystemFile::Seek(SeekSizeType offset, SeekMode mode)
Platform::Seek(m_handle, this, offset, mode);
}
SystemFile::SizeType SystemFile::Tell()
SystemFile::SizeType SystemFile::Tell() const
{
return Platform::Tell(m_handle, this);
}
bool SystemFile::Eof()
bool SystemFile::Eof() const
{
return Platform::Eof(m_handle, this);
}
+2 -2
View File
@@ -72,9 +72,9 @@ namespace AZ
/// Seek in current file.
void Seek(SeekSizeType offset, SeekMode mode);
/// Get the cursor position in the current file.
SizeType Tell();
SizeType Tell() const;
/// Is the cursor at the end of the file?
bool Eof();
bool Eof() const;
/// Get the time the file was last modified.
AZ::u64 ModificationTime();
/// Read data from a file synchronous. Return number of bytes actually read in the buffer.
@@ -353,6 +353,7 @@ namespace AZ
Method("CreateFromMatrix3x3AndTranslation", &Transform::CreateFromMatrix3x3AndTranslation)->
Method("CreateUniformScale", &Transform::CreateUniformScale)->
Method("CreateTranslation", &Transform::CreateTranslation)->
Method("CreateLookAt", &Transform::CreateLookAt)->
Method("ConstructFromValuesNumeric", &Internal::ConstructTransformFromValues);
}
}
@@ -10,6 +10,7 @@
#include <cerrno>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileReader.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/error/en.h>
#include <AzCore/NativeUI//NativeUIRequests.h>
@@ -1116,118 +1117,6 @@ namespace AZ
}
}
//! Structure which encapsulates Commands to either the FileIOBase or SystemFile classes based on
//! the SettingsRegistry option to use FileIO
struct SettingsRegistryFileReader
{
using FileHandleType = AZStd::variant<AZStd::monostate, AZ::IO::SystemFile, AZ::IO::HandleType>;
SettingsRegistryFileReader() = default;
SettingsRegistryFileReader(bool useFileIo, const char* filePath)
{
Open(useFileIo, filePath);
}
~SettingsRegistryFileReader()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
{
fileIo->Close(*fileHandle);
}
}
}
bool Open(bool useFileIo, const char* filePath)
{
Close();
if (AZ::IO::FileIOBase* fileIo = useFileIo ? AZ::IO::FileIOBase::GetInstance() : nullptr; fileIo != nullptr)
{
AZ::IO::HandleType fileHandle;
if (fileIo->Open(filePath, IO::OpenMode::ModeRead, fileHandle))
{
m_file = fileHandle;
return true;
}
}
else
{
AZ::IO::SystemFile file;
if (file.Open(filePath, IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
{
m_file = AZStd::move(file);
return true;
}
}
return false;
}
bool IsOpen() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
return *fileHandle != AZ::IO::InvalidHandle;
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->IsOpen();
}
return false;
}
void Close()
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (AZ::IO::FileIOBase* fileIo = AZ::IO::FileIOBase::GetInstance(); fileIo != nullptr)
{
fileIo->Close(*fileHandle);
}
}
m_file = AZStd::monostate{};
}
u64 Length() const
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (u64 fileSize{}; AZ::IO::FileIOBase::GetInstance()->Size(*fileHandle, fileSize))
{
return fileSize;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Length();
}
return 0;
}
AZ::IO::SizeType Read(AZ::IO::SizeType byteSize, void* buffer)
{
if (auto fileHandle = AZStd::get_if<AZ::IO::HandleType>(&m_file); fileHandle != nullptr)
{
if (AZ::u64 bytesRead{}; AZ::IO::FileIOBase::GetInstance()->Read(*fileHandle, buffer, byteSize, false, &bytesRead))
{
return bytesRead;
}
}
else if (auto systemFile = AZStd::get_if<AZ::IO::SystemFile>(&m_file); systemFile != nullptr)
{
return systemFile->Read(byteSize, buffer);
}
return 0;
}
FileHandleType m_file;
};
bool SettingsRegistryImpl::MergeSettingsFileInternal(const char* path, Format format, AZStd::string_view rootKey,
AZStd::vector<char>& scratchBuffer)
{
@@ -1236,7 +1125,7 @@ namespace AZ
Pointer pointer(AZ_SETTINGS_REGISTRY_HISTORY_KEY "/-");
SettingsRegistryFileReader fileReader(m_useFileIo, path);
FileReader fileReader(m_useFileIo ? AZ::IO::FileIOBase::GetInstance(): nullptr, path);
if (!fileReader.IsOpen())
{
AZ_Error("Settings Registry", false, R"(Unable to open registry file "%s".)", path);
@@ -6,6 +6,8 @@
*
*/
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/FileReader.h>
#include <AzCore/IO/GenericStreams.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/TextStreamWriters.h>
@@ -388,8 +390,36 @@ namespace AZ::SettingsRegistryMergeUtils
const ConfigParserSettings& configParserSettings)
{
auto configPath = FindEngineRoot(registry) / filePath;
IO::SystemFile configFile;
if (!configFile.Open(configPath.c_str(), IO::SystemFile::OpenMode::SF_OPEN_READ_ONLY))
IO::FileReader configFile;
bool configFileOpened{};
switch (configParserSettings.m_fileReaderClass)
{
case ConfigParserSettings::FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile:
{
auto fileIo = AZ::IO::FileIOBase::GetInstance();
configFileOpened = configFile.Open(fileIo, configPath.c_str());
break;
}
case ConfigParserSettings::FileReaderClass::UseSystemFileOnly:
{
configFileOpened = configFile.Open(nullptr, configPath.c_str());
break;
}
case ConfigParserSettings::FileReaderClass::UseFileIOOnly:
{
auto fileIo = AZ::IO::FileIOBase::GetInstance();
if (fileIo == nullptr)
{
return false;
}
configFileOpened = configFile.Open(fileIo, configPath.c_str());
break;
}
default:
AZ_Error("SettingsRegistryMergeUtils", false, "An Invalid FileReaderClass enum value has been supplied");
return false;
}
if (!configFileOpened)
{
AZ_Warning("SettingsRegistryMergeUtils", false, R"(Unable to open file "%s")", configPath.c_str());
return false;
@@ -480,7 +510,7 @@ namespace AZ::SettingsRegistryMergeUtils
AZ_Error("SettingsRegistryMergeUtils", false,
R"(The config file "%s" contains a line which is longer than the max line length of %zu.)" "\n"
R"(Parsing will halt. The line content so far is:)" "\n"
R"("%.*s")" "\n", configFile.Name(), configBuffer.max_size(),
R"("%.*s")" "\n", configPath.c_str(), configBuffer.max_size(),
aznumeric_cast<int>(configBuffer.size()), configBuffer.data());
configFileParsed = false;
break;
@@ -155,6 +155,15 @@ namespace AZ::SettingsRegistryMergeUtils
//! structure which is forwarded to the SettingsRegistryInterface MergeCommandLineArgument function
//! The structure contains a functor which returns true if a character is a valid delimiter
SettingsRegistryInterface::CommandLineArgumentSettings m_commandLineSettings;
//! enumeration to indicate if AZ::IO::FileIOBase should be used to open the config file over AZ::IO::SystemFile
enum class FileReaderClass
{
UseFileIOIfAvailableFallbackToSystemFile,
UseSystemFileOnly,
UseFileIOOnly
};
FileReaderClass m_fileReaderClass = FileReaderClass::UseFileIOIfAvailableFallbackToSystemFile;
};
//! Loads basic configuration files which have structures similar to Windows INI files
//! It is inspired by the Python configparser module: https://docs.python.org/3.10/library/configparser.html
@@ -166,6 +166,8 @@ set(FILES
IO/FileIO.cpp
IO/FileIO.h
IO/FileIOEventBus.h
IO/FileReader.cpp
IO/FileReader.h
IO/IOUtils.h
IO/IOUtils.cpp
IO/IStreamer.h
@@ -0,0 +1,72 @@
/*
* 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/IO/FileReader.h>
#include <FileIOBaseTestTypes.h>
#include <AzCore/UnitTest/TestTypes.h>
namespace UnitTest
{
template <typename FileIOType>
class FileReaderTestFixture
: public ScopedAllocatorSetupFixture
{
public:
void SetUp() override
{
if constexpr (AZStd::is_same_v<FileIOType, TestFileIOBase>)
{
m_fileIo = AZStd::make_unique<TestFileIOBase>();
}
}
void TearDown() override
{
m_fileIo.reset();
}
protected:
AZStd::unique_ptr<AZ::IO::FileIOBase> m_fileIo{};
};
using FileIOTypes = ::testing::Types<void, TestFileIOBase>;
TYPED_TEST_CASE(FileReaderTestFixture, FileIOTypes);
TYPED_TEST(FileReaderTestFixture, ConstructorWithFilePath_OpensFileSuccessfully)
{
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
EXPECT_TRUE(fileReader.IsOpen());
}
TYPED_TEST(FileReaderTestFixture, Open_OpensFileSucessfully)
{
AZ::IO::FileReader fileReader;
fileReader.Open(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
EXPECT_TRUE(fileReader.IsOpen());
}
TYPED_TEST(FileReaderTestFixture, Eof_OnNULDeviceFile_Succeeds)
{
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
EXPECT_TRUE(fileReader.Eof());
}
TYPED_TEST(FileReaderTestFixture, GetFilePath_ReturnsNULDeviceFilename_Succeeds)
{
AZ::IO::FileReader fileReader(this->m_fileIo.get(), AZ::IO::SystemFile::GetNullFilename());
AZ::IO::FixedMaxPath filePath;
EXPECT_TRUE(fileReader.GetFilePath(filePath));
AZ::IO::FixedMaxPath nulFilename{ AZ::IO::SystemFile::GetNullFilename() };
if (this->m_fileIo)
{
EXPECT_TRUE(this->m_fileIo->ResolvePath(nulFilename, nulFilename));
}
EXPECT_EQ(nulFilename, filePath);
}
} // namespace UnitTest
@@ -37,6 +37,7 @@ set(FILES
FileIOBaseTestTypes.h
Geometry2DUtils.cpp
Interface.cpp
IO/FileReaderTests.cpp
IO/Path/PathTests.cpp
IPC.cpp
Jobs.cpp
@@ -28,6 +28,7 @@
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
#include <AzToolsFramework/Slice/SliceMetadataEntityContextComponent.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h>
@@ -248,6 +249,7 @@ namespace AzToolsFramework
components.insert(components.end(), {
azrtti_typeid<EditorEntityContextComponent>(),
azrtti_typeid<Components::EditorEntityUiSystemComponent>(),
azrtti_typeid<FocusModeSystemComponent>(),
azrtti_typeid<SliceMetadataEntityContextComponent>(),
azrtti_typeid<Prefab::PrefabSystemComponent>(),
azrtti_typeid<EditorEntityFixupComponent>(),
@@ -138,7 +138,13 @@ namespace AzToolsFramework
m_indexMap[row] = index;
m_rowMap[index] = row;
++row;
++m_displayedItemsCounter;
// We only want to increase the displayed counter if it is a parent (Source)
// so we don't cut children entries.
if (entry->GetEntryType() == AssetBrowserEntry::AssetEntryType::Source)
{
++m_displayedItemsCounter;
}
}
if (model->hasChildren(index))
@@ -29,20 +29,20 @@ namespace AzToolsFramework
{
AssetBrowserTableView::AssetBrowserTableView(QWidget* parent)
: AzQtComponents::TableView(parent)
, m_delegate(new EntryDelegate(this))
, m_delegate(new SearchEntryDelegate(this))
{
setSortingEnabled(true);
setSortingEnabled(false);
setItemDelegate(m_delegate);
setRootIsDecorated(false);
//Styling the header aligning text to the left and using a bold font.
header()->setDefaultAlignment(Qt::AlignLeft);
header()->setStyleSheet("QHeaderView { font-weight: bold; }");
header()->setStyleSheet("QHeaderView { font-weight: bold; };");
setContextMenuPolicy(Qt::CustomContextMenu);
setMouseTracking(true);
setSortingEnabled(false);
setSelectionMode(QAbstractItemView::SingleSelection);
connect(this, &AzQtComponents::TableView::customContextMenuRequested, this, &AssetBrowserTableView::OnContextMenu);
@@ -67,6 +67,8 @@ namespace AzToolsFramework
header()->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch);
header()->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch);
header()->setSortIndicatorShown(false);
header()->setSectionsClickable(false);
}
void AssetBrowserTableView::SetName(const QString& name)
@@ -26,7 +26,7 @@ namespace AzToolsFramework
class AssetBrowserEntry;
class AssetBrowserTableModel;
class AssetBrowserFilterModel;
class EntryDelegate;
class SearchEntryDelegate;
class AssetBrowserTableView //! Table view that displays the asset browser entries in a list.
: public AzQtComponents::TableView
@@ -67,9 +67,9 @@ namespace AzToolsFramework
private:
QString m_name;
QPointer<AssetBrowserTableModel> m_tableModel = nullptr;
QPointer<AssetBrowserFilterModel> m_sourceFilterModel = nullptr;
EntryDelegate* m_delegate = nullptr;
QPointer<AssetBrowserTableModel> m_tableModel;
QPointer<AssetBrowserFilterModel> m_sourceFilterModel;
SearchEntryDelegate* m_delegate = nullptr;
private Q_SLOTS:
void OnContextMenu(const QPoint& point);
@@ -11,12 +11,13 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/Thumbnails/ThumbnailerBus.h>
#include <AzToolsFramework/AssetBrowser/Views/EntryDelegate.h>
#include <AzCore/Utils/Utils.h>
#include <AzQtComponents/Components/StyledBusyLabel.h>
#include <QApplication>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
// 4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
#include <QAbstractItemView>
#include <QPainter>
AZ_POP_DISABLE_WARNING
@@ -24,8 +25,13 @@ namespace AzToolsFramework
{
namespace AssetBrowser
{
const int ENTRY_SPACING_LEFT_PIXELS = 8;
const int ENTRY_ICON_MARGIN_LEFT_PIXELS = 2;
static constexpr const char* TreeIconPathFirst = "Assets/Editor/Icons/AssetBrowser/TreeBranch_First.svg";
static constexpr const char* TreeIconPathMiddle = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Middle.svg";
static constexpr const char* TreeIconPathLast = "Assets/Editor/Icons/AssetBrowser/TreeBranch_Last.svg";
static constexpr const char* TreeIconPathOneChild = "Assets/Editor/Icons/AssetBrowser/TreeBranch_OneChild.svg";
const int EntrySpacingLeftPixels = 8;
const int EntryIconMarginLeftPixels = 2;
EntryDelegate::EntryDelegate(QWidget* parent)
: QStyledItemDelegate(parent)
@@ -62,7 +68,7 @@ namespace AzToolsFramework
// Draw main entry thumbnail.
QRect remainingRect(option.rect);
remainingRect.adjust(ENTRY_ICON_MARGIN_LEFT_PIXELS, 0, 0, 0); // bump it rightwards to give some margin to the icon.
remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon.
QSize iconSize(m_iconSize, m_iconSize);
// Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size
@@ -89,7 +95,7 @@ namespace AzToolsFramework
}
remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
remainingRect.adjust(ENTRY_SPACING_LEFT_PIXELS, 0, 0, 0); // bump it to the right by the spacing.
remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing.
}
QString displayString = index.column() == aznumeric_cast<int>(AssetBrowserEntry::Column::Name)
? qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Name)))
@@ -148,7 +154,162 @@ namespace AzToolsFramework
return m_iconSize;
}
} // namespace Thumbnailer
SearchEntryDelegate::SearchEntryDelegate(QWidget* parent)
: EntryDelegate(parent)
{
LoadBranchPixMaps();
}
void SearchEntryDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto data = index.data(AssetBrowserModel::Roles::EntryRole);
if (data.canConvert<const AssetBrowserEntry*>())
{
bool isEnabled = (option.state & QStyle::State_Enabled) != 0;
bool isSelected = (option.state & QStyle::State_Selected) != 0;
QStyle* style = option.widget ? option.widget->style() : QApplication::style();
// draw the background
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, option.widget);
// Draw main entry thumbnail.
QRect remainingRect(option.rect);
QSize iconSize(m_iconSize, m_iconSize);
// Note that the thumbnail might actually be smaller than the row if theres a lot of padding or font size
// so it needs to center vertically with padding in that case:
QPoint iconTopLeft;
QPoint branchIconTopLeft = QPoint();
auto entry = qvariant_cast<const AssetBrowserEntry*>(data);
auto sourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(entry);
//If it is a SourceEntry or it is not the column name we don't want to add space for the branch Icon
if (sourceEntry || index.column() != aznumeric_cast<int>(AssetBrowserEntry::Column::Name))
{
remainingRect.adjust(EntryIconMarginLeftPixels, 0, 0, 0); // bump it rightwards to give some margin to the icon.
iconTopLeft = QPoint(remainingRect.x(), remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
}
else
{
remainingRect.adjust(EntryIconMarginLeftPixels + m_iconSize, 0, 0, 0); // bump it rightwards to give some margin to the icon.
iconTopLeft = QPoint(remainingRect.x() / 2 + m_iconSize, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
branchIconTopLeft = QPoint((remainingRect.x() / 2) - 2, remainingRect.y() + (remainingRect.height() / 2) - (m_iconSize / 2));
}
QPalette actualPalette(option.palette);
if (index.column() == aznumeric_cast<int>(AssetBrowserEntry::Column::Name))
{
int thumbX = DrawThumbnail(painter, iconTopLeft, iconSize, entry->GetThumbnailKey());
if (sourceEntry)
{
if (m_showSourceControl)
{
DrawThumbnail(painter, iconTopLeft, iconSize, sourceEntry->GetSourceControlThumbnailKey());
}
// sources with no children should be greyed out.
if (sourceEntry->GetChildCount() == 0)
{
isEnabled = false; // draw in disabled style.
actualPalette.setCurrentColorGroup(QPalette::Disabled);
}
}
else
{
//Get the indexes above and below our entry to see what type are they.
QAbstractItemView* view = qobject_cast<QAbstractItemView*>(option.styleObject);
const QAbstractItemModel* viewModel = view->model();
const QModelIndex indexBelow = viewModel->index(index.row() + 1, index.column());
const QModelIndex indexAbove = viewModel->index(index.row() - 1, index.column());
auto aboveEntry = qvariant_cast<const AssetBrowserEntry*>(indexBelow.data(AssetBrowserModel::Roles::EntryRole));
auto belowEntry = qvariant_cast<const AssetBrowserEntry*>(indexAbove.data(AssetBrowserModel::Roles::EntryRole));
auto aboveSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(aboveEntry);
auto belowSourceEntry = azrtti_cast<const SourceAssetBrowserEntry*>(belowEntry);
// if current index is the last entry in the view
// or the index above it is a Source Entry and
// the index below is invalid or is valid but it is also a source entry
// then the current index is the only child.
if (index.row() == viewModel->rowCount() - 1 ||
(indexBelow.isValid() && aboveSourceEntry &&
(!indexAbove.isValid() || (indexAbove.isValid() && belowSourceEntry))))
{
DrawBranchPixMap(EntryBranchType::OneChild, painter, branchIconTopLeft, iconSize); // Draw One Child Icon
}
else if (indexBelow.isValid() && aboveSourceEntry) // The index above is a source entry
{
DrawBranchPixMap(EntryBranchType::Last, painter, branchIconTopLeft, iconSize); // Draw First child Icon
}
else if (indexAbove.isValid() && belowSourceEntry) // The index below is a source entry
{
DrawBranchPixMap(EntryBranchType::First, painter, branchIconTopLeft, iconSize); // Draw Last Child Icon
}
else //the index above and below are also child entries
{
DrawBranchPixMap(EntryBranchType::Middle, painter, branchIconTopLeft, iconSize); // Draw Default child Icon.
}
}
remainingRect.adjust(thumbX, 0, 0, 0); // bump it to the right by the size of the thumbnail
remainingRect.adjust(EntrySpacingLeftPixels, 0, 0, 0); // bump it to the right by the spacing.
}
QString displayString = index.column() == aznumeric_cast<int>(AssetBrowserEntry::Column::Name)
? qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Name)))
: qvariant_cast<QString>(entry->data(aznumeric_cast<int>(AssetBrowserEntry::Column::Path)));
style->drawItemText(
painter, remainingRect, option.displayAlignment, actualPalette, isEnabled, displayString,
isSelected ? QPalette::HighlightedText : QPalette::Text);
}
}
void SearchEntryDelegate::LoadBranchPixMaps()
{
AZ::IO::BasicPath<AZ::IO::FixedMaxPathString> absoluteIconPath;
for (int branchType = EntryBranchType::First; branchType != EntryBranchType::Count; ++branchType)
{
QPixmap pixmap;
switch (branchType)
{
case AzToolsFramework::AssetBrowser::EntryBranchType::First:
absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathFirst;
break;
case AzToolsFramework::AssetBrowser::EntryBranchType::Middle:
absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathMiddle;
break;
case AzToolsFramework::AssetBrowser::EntryBranchType::Last:
absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathLast;
break;
case AzToolsFramework::AssetBrowser::EntryBranchType::OneChild:
default:
absoluteIconPath = AZ::IO::FixedMaxPath(AZ::Utils::GetEnginePath()) / TreeIconPathOneChild;
break;
}
bool pixmapLoadedSuccess = pixmap.load(absoluteIconPath.c_str());
AZ_Assert(pixmapLoadedSuccess, "Error loading Branch Icons in SearchEntryDelegate");
m_branchIcons[static_cast<EntryBranchType>(branchType)] = pixmap;
}
}
void SearchEntryDelegate::DrawBranchPixMap(
EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const
{
const QPixmap& pixmap = m_branchIcons[branchType];
pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
const QSize sizeDelta = size - pixmap.size();
const QPoint pointDelta = QPoint(sizeDelta.width() / 2, sizeDelta.height() / 2);
painter->drawPixmap(point + pointDelta, pixmap);
}
} // namespace AssetBrowser
} // namespace AzToolsFramework
#include "AssetBrowser/Views/moc_EntryDelegate.cpp"
@@ -27,9 +27,19 @@ namespace AzToolsFramework
{
namespace AssetBrowser
{
//! Type of branch icon the delegate should paint.
enum EntryBranchType
{
First,
Middle,
Last,
OneChild,
Count
};
class AssetBrowserFilterModel;
//! EntryDelegate draws a single item in AssetBrowser
//! EntryDelegate draws a single item in AssetBrowser.
class EntryDelegate
: public QStyledItemDelegate
{
@@ -52,5 +62,23 @@ namespace AzToolsFramework
//! Draw a thumbnail and return its width
int DrawThumbnail(QPainter* painter, const QPoint& point, const QSize& size, Thumbnailer::SharedThumbnailKey thumbnailKey) const;
};
//! SearchEntryDelegate draws a single item in AssetBrowserTableView.
class SearchEntryDelegate
: public EntryDelegate
{
Q_OBJECT
public:
explicit SearchEntryDelegate(QWidget* parent = nullptr);
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
private:
void LoadBranchPixMaps();
void DrawBranchPixMap(EntryBranchType branchType, QPainter* painter, const QPoint& point, const QSize& size) const;
private:
QMap<EntryBranchType, QPixmap> m_branchIcons;
};
} // namespace AssetBrowser
} // namespace AzToolsFramework
@@ -22,6 +22,7 @@
#include <AzToolsFramework/Entity/EditorEntityModelComponent.h>
#include <AzToolsFramework/Entity/EditorEntitySearchComponent.h>
#include <AzToolsFramework/Entity/EditorEntitySortComponent.h>
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
#include <AzToolsFramework/PropertyTreeEditor/PropertyTreeEditorComponent.h>
#include <AzToolsFramework/Render/EditorIntersectorComponent.h>
#include <AzToolsFramework/Slice/SliceDependencyBrowserComponent.h>
@@ -69,6 +70,7 @@ namespace AzToolsFramework
Components::EditorSelectionAccentSystemComponent::CreateDescriptor(),
EditorEntityContextComponent::CreateDescriptor(),
EditorEntityFixupComponent::CreateDescriptor(),
FocusModeSystemComponent::CreateDescriptor(),
SliceMetadataEntityContextComponent::CreateDescriptor(),
SliceRequestComponent::CreateDescriptor(),
Prefab::PrefabSystemComponent::CreateDescriptor(),
@@ -0,0 +1,39 @@
/*
* 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/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzToolsFramework
{
//! FocusModeInterface
//! Interface to handle the Editor Focus Mode.
class FocusModeInterface
{
public:
AZ_RTTI(FocusModeInterface, "{437243B0-F86B-422F-B7B8-4A21CC000702}");
//! Sets the root entity the Editor should focus on.
//! The Editor will only allow the user to select entities that are descendants of the EntityId provided.
//! @param entityId The entityId that will become the new focus root.
virtual void SetFocusRoot(AZ::EntityId entityId) = 0;
//! Clears the Editor focus, allowing the user to select the whole level again.
virtual void ClearFocusRoot() = 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;
//! Returns whether the entity id provided is part of the focused sub-tree.
virtual bool IsInFocusSubTree(AZ::EntityId entityId) const = 0;
};
} // namespace AzToolsFramework
@@ -0,0 +1,92 @@
/*
* 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/Component/TransformBus.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
namespace AzToolsFramework
{
bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId)
{
if (entityId == AZ::EntityId())
{
return false;
}
if (entityId == focusRootId)
{
return true;
}
AZ::EntityId parentId;
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformInterface::GetParentId);
return IsInFocusSubTree(parentId, focusRootId);
}
void FocusModeSystemComponent::Init()
{
}
void FocusModeSystemComponent::Activate()
{
AZ::Interface<FocusModeInterface>::Register(this);
}
void FocusModeSystemComponent::Deactivate()
{
AZ::Interface<FocusModeInterface>::Unregister(this);
}
void FocusModeSystemComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
{
}
void FocusModeSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("EditorFocusMode"));
}
void FocusModeSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
{
}
void FocusModeSystemComponent::GetIncompatibleServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
}
void FocusModeSystemComponent::SetFocusRoot(AZ::EntityId entityId)
{
m_focusRoot = entityId;
// TODO - If m_focusRoot != AZ::EntityId(), activate focus mode via ViewportEditorModeTrackerInterface; else, deactivate focus mode
}
void FocusModeSystemComponent::ClearFocusRoot()
{
SetFocusRoot(AZ::EntityId());
}
AZ::EntityId FocusModeSystemComponent::GetFocusRoot()
{
return m_focusRoot;
}
bool FocusModeSystemComponent::IsInFocusSubTree(AZ::EntityId entityId) const
{
if (m_focusRoot == AZ::EntityId())
{
return true;
}
return AzToolsFramework::IsInFocusSubTree(entityId, m_focusRoot);
}
} // namespace AzToolsFramework
@@ -0,0 +1,51 @@
/*
* 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/Component.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
namespace AzToolsFramework
{
bool IsInFocusSubTree(AZ::EntityId entityId, AZ::EntityId focusRootId);
//! System Component to handle the Editor Focus Mode system
class FocusModeSystemComponent final
: public AZ::Component
, private FocusModeInterface
{
public:
AZ_COMPONENT(FocusModeSystemComponent, "{6CE522FE-2057-4794-BD05-61E04BD8EA30}");
FocusModeSystemComponent() = default;
virtual ~FocusModeSystemComponent() = default;
// AZ::Component overrides ...
void Init() override;
void Activate() override;
void Deactivate() override;
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
// FocusModeInterface overrides ...
void SetFocusRoot(AZ::EntityId entityId) override;
void ClearFocusRoot() override;
AZ::EntityId GetFocusRoot() override;
bool IsInFocusSubTree(AZ::EntityId entityId) const override;
private:
AZ::EntityId m_focusRoot;
};
} // namespace AzToolsFramework
@@ -0,0 +1,102 @@
/*
* 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/Prefab/PrefabFocusHandler.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
namespace AzToolsFramework::Prefab
{
PrefabFocusHandler::PrefabFocusHandler()
{
m_instanceEntityMapperInterface = AZ::Interface<InstanceEntityMapperInterface>::Get();
AZ_Assert(
m_instanceEntityMapperInterface,
"Prefab - PrefabFocusHandler - "
"Instance Entity Mapper Interface could not be found. "
"Check that it is being correctly initialized.");
AZ::Interface<PrefabFocusInterface>::Register(this);
}
PrefabFocusHandler::~PrefabFocusHandler()
{
AZ::Interface<PrefabFocusInterface>::Unregister(this);
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
{
InstanceOptionalReference focusedInstance;
if (!entityId.IsValid())
{
PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
if(!prefabEditorEntityOwnershipInterface)
{
return AZ::Failure(AZStd::string("Could not focus on root prefab instance - internal error "
"(PrefabEditorEntityOwnershipInterface unavailable)."));
}
focusedInstance = prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
}
else
{
focusedInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
}
if (!focusedInstance.has_value())
{
return AZ::Failure(AZStd::string(
"Prefab Focus Handler: Couldn't find owning instance of entityId provided."));
}
m_focusedInstance = focusedInstance;
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
if (focusModeInterface)
{
focusModeInterface->SetFocusRoot(focusedInstance->get().GetContainerEntityId());
}
return AZ::Success();
}
TemplateId PrefabFocusHandler::GetFocusedPrefabTemplateId()
{
return m_focusedTemplateId;
}
InstanceOptionalReference PrefabFocusHandler::GetFocusedPrefabInstance()
{
return m_focusedInstance;
}
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId)
{
if (!m_focusedInstance.has_value())
{
// PrefabFocusHandler has not been initialized yet.
return false;
}
if (!entityId.IsValid())
{
return false;
}
InstanceOptionalReference instance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
return instance.has_value() && (&instance->get() == &m_focusedInstance->get());
}
} // namespace AzToolsFramework::Prefab
@@ -0,0 +1,44 @@
/*
* 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/Memory/SystemAllocator.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
namespace AzToolsFramework::Prefab
{
class InstanceEntityMapperInterface;
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
class PrefabFocusHandler final
: private PrefabFocusInterface
{
public:
AZ_CLASS_ALLOCATOR(PrefabFocusHandler, AZ::SystemAllocator, 0);
PrefabFocusHandler();
~PrefabFocusHandler();
// PrefabFocusInterface override ...
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
TemplateId GetFocusedPrefabTemplateId() override;
InstanceOptionalReference GetFocusedPrefabInstance() override;
bool IsOwningPrefabBeingFocused(AZ::EntityId entityId) override;
private:
InstanceOptionalReference m_focusedInstance;
TemplateId m_focusedTemplateId;
InstanceEntityMapperInterface* m_instanceEntityMapperInterface;
};
} // namespace AzToolsFramework::Prefab
@@ -0,0 +1,43 @@
/*
* 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/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
namespace AzToolsFramework::Prefab
{
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
//! Interface to handle operations related to the Prefab Focus system.
class PrefabFocusInterface
{
public:
AZ_RTTI(PrefabFocusInterface, "{F3CFA37B-5FD8-436A-9C30-60EB54E350E1}");
//! Set the focused prefab instance to the owning instance of the entityId provided.
//! @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;
//! Returns the template id of the instance the prefab system is focusing on.
virtual TemplateId GetFocusedPrefabTemplateId() = 0;
//! Returns a reference to the instance the prefab system is focusing on.
virtual InstanceOptionalReference GetFocusedPrefabInstance() = 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;
};
} // namespace AzToolsFramework::Prefab
@@ -13,6 +13,7 @@
#include <AzCore/std/string/string_view.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/PrefabFocusHandler.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabUndoCache.h>
@@ -189,6 +190,9 @@ namespace AzToolsFramework
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
// Handles the Prefab Focus API that determines what prefab is being edited.
PrefabFocusHandler m_prefabFocusHandler;
// Caches entity states for undo/redo purposes
PrefabUndoCache m_prefabUndoCache;
@@ -369,7 +369,7 @@ namespace AzToolsFramework
// A counter for generating unique Link Ids.
AZStd::atomic<LinkId> m_linkIdCounter = 0u;
// Used for finding the owning instance of an arbitrary entity
// Used for finding the owning instance of an arbitrary entity.
InstanceEntityMapper m_instanceEntityMapper;
// Used for finding the Instances owned by an arbitrary Template.
@@ -378,16 +378,16 @@ namespace AzToolsFramework
// Used for loading/saving Prefab Template files.
PrefabLoader m_prefabLoader;
// Handler the public Prefab API used by UI and scripting
// Handles the public Prefab API used by UI and scripting.
PrefabPublicHandler m_prefabPublicHandler;
// Used for updating Instances of Prefab Template.
InstanceUpdateExecutor m_instanceUpdateExecutor;
// Used for updating Templates when Instances are modified
// Used for updating Templates when Instances are modified.
InstanceToTemplatePropagator m_instanceToTemplatePropagator;
// Handler of the public Prefab requests
// Handler of the public Prefab requests.
PrefabPublicRequestHandler m_prefabPublicRequestHandler;
};
} // namespace Prefab
@@ -18,18 +18,19 @@ AzToolsFramework--EntityOutlinerWidget QTreeView
selection-background-color: transparent;
}
/*
* Entity Outliner handles hover and selected state of items via code,
* so we need to override the AzQtComponents::Treeview style.
*/
AzToolsFramework--EntityOutlinerWidget QTreeView::branch:hover
, AzToolsFramework--EntityOutlinerWidget QTreeView::item:hover
{
background: rgba(255, 255, 255, 30);
}
AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected
, AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected
, AzToolsFramework--EntityOutlinerWidget QTreeView::item:selected
, AzToolsFramework--EntityOutlinerWidget QTreeView::branch:selected:active
, AzToolsFramework--EntityOutlinerWidget QTreeView::item:selected:active
{
background: rgba(255, 255, 255, 45);
background: transparent;
}
@@ -44,6 +44,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/ToolsComponents/ComponentAssetMimeDataContainer.h>
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
#include <AzToolsFramework/ToolsComponents/EditorEntityIdContainer.h>
@@ -102,10 +103,14 @@ namespace AzToolsFramework
EntityCompositionNotificationBus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
m_editorEntityFrameworkInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(m_editorEntityUiInterface != nullptr,
"EntityOutlinerListModel requires a EditorEntityUiInterface instance on Initialize.");
AZ_Assert(m_editorEntityFrameworkInterface != nullptr,
"EntityOutlinerListModel requires a EditorEntityFrameworkInterface instance on Initialize.");
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
AZ_Assert(
m_focusModeInterface != nullptr,
"EntityOutlinerListModel requires a FocusModeInterface instance on Initialize.");
}
int EntityOutlinerListModel::rowCount(const QModelIndex& parent) const
@@ -279,7 +284,7 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::GetEntityIcon(const AZ::EntityId& id) const
{
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(id);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(id);
QIcon icon;
// Retrieve the icon from the handler
@@ -316,7 +321,7 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::GetEntityTooltip(const AZ::EntityId& id) const
{
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(id);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(id);
QString tooltip;
// Retrieve the tooltip from the handler
@@ -349,7 +354,7 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::dataForVisibility(const QModelIndex& index, int role) const
{
auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
@@ -377,7 +382,7 @@ namespace AzToolsFramework
QVariant EntityOutlinerListModel::dataForLock(const QModelIndex& index, int role) const
{
auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
@@ -436,7 +441,7 @@ namespace AzToolsFramework
if (value.canConvert<Qt::CheckState>())
{
const auto entityId = GetEntityFromIndex(index);
auto entityUiHandler = m_editorEntityFrameworkInterface->GetHandler(entityId);
auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId);
if (!entityUiHandler || entityUiHandler->CanToggleLockVisibility(entityId))
{
@@ -532,6 +537,11 @@ namespace AzToolsFramework
break;
}
if (AZ::EntityId entityId = GetEntityFromIndex(index); !m_focusModeInterface->IsInFocusSubTree(entityId))
{
itemFlags &= !Qt::ItemIsEnabled;
}
return itemFlags;
}
@@ -36,6 +36,7 @@
namespace AzToolsFramework
{
class EditorEntityUiInterface;
class FocusModeInterface;
namespace EntityOutliner
{
@@ -273,7 +274,8 @@ namespace AzToolsFramework
QVariant GetEntityIcon(const AZ::EntityId& id) const;
QVariant GetEntityTooltip(const AZ::EntityId& id) const;
EditorEntityUiInterface* m_editorEntityFrameworkInterface = nullptr;
EditorEntityUiInterface* m_editorEntityUiInterface = nullptr;
FocusModeInterface* m_focusModeInterface = nullptr;
};
class EntityOutlinerCheckBox
@@ -38,6 +38,8 @@ namespace AzToolsFramework
AZ_Assert((m_editorEntityFrameworkInterface != nullptr),
"EntityOutlinerTreeView requires a EditorEntityFrameworkInterface instance on Construction.");
viewport()->setMouseTracking(true);
}
EntityOutlinerTreeView::~EntityOutlinerTreeView()
@@ -59,6 +61,11 @@ namespace AzToolsFramework
}
}
void EntityOutlinerTreeView::leaveEvent([[maybe_unused]] QEvent* event)
{
m_mousePosition = QPoint();
}
void EntityOutlinerTreeView::mousePressEvent(QMouseEvent* event)
{
//postponing normal mouse pressed logic until mouse is released or dragged
@@ -112,6 +119,8 @@ namespace AzToolsFramework
setSelectionMode(selectionModeBefore);
}
m_mousePosition = event->pos();
//process mouse movement as normal, potentially triggering drag and drop
QTreeView::mouseMoveEvent(event);
}
@@ -172,12 +181,45 @@ namespace AzToolsFramework
void EntityOutlinerTreeView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const
{
const bool isEnabled = (this->model()->flags(index) & Qt::ItemIsEnabled);
const bool isSelected = selectionModel()->isSelected(index);
const bool isHovered = (index == indexAt(m_mousePosition)) && isEnabled;
// Paint the branch Selection/Hover Rect
PaintBranchSelectionHoverRect(painter, rect, isSelected, isHovered);
// Paint the branch background as defined by the entity's handler, or its closes ancestor's.
PaintBranchBackground(painter, rect, index);
QTreeView::drawBranches(painter, rect, index);
}
void EntityOutlinerTreeView::PaintBranchSelectionHoverRect(
QPainter* painter, const QRect& rect, bool isSelected, bool isHovered) const
{
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
if (isSelected || isHovered)
{
QPainterPath backgroundPath;
QRect backgroundRect(rect);
backgroundPath.addRect(backgroundRect);
QColor backgroundColor = m_hoverColor;
if (isSelected)
{
backgroundColor = m_selectedColor;
}
painter->fillPath(backgroundPath, backgroundColor);
}
painter->restore();
}
void EntityOutlinerTreeView::PaintBranchBackground(QPainter* painter, const QRect& rect, const QModelIndex& index) const
{
// Go through ancestors and add them to the stack
@@ -59,6 +59,7 @@ namespace AzToolsFramework
void startDrag(Qt::DropActions supportedActions) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dropEvent(QDropEvent* event) override;
void leaveEvent(QEvent* event) 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;
@@ -72,8 +73,10 @@ namespace AzToolsFramework
void StartCustomDrag(const QModelIndexList& indexList, Qt::DropActions supportedActions) override;
void PaintBranchBackground(QPainter* painter, const QRect& rect, const QModelIndex& index) const;
void PaintBranchSelectionHoverRect(QPainter* painter, const QRect& rect, bool isSelected, bool isHovered) const;
QMouseEvent* m_queuedMouseEvent;
QPoint m_mousePosition;
bool m_draggingUnselectedItem; // This is set when an item is dragged outside its bounding box.
int m_expandOnlyDelay = -1;
@@ -8,7 +8,6 @@
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
@@ -24,14 +23,6 @@ namespace AzToolsFramework
LevelRootUiHandler::LevelRootUiHandler()
{
m_prefabEditInterface = AZ::Interface<Prefab::PrefabEditInterface>::Get();
if (m_prefabEditInterface == nullptr)
{
AZ_Assert(false, "LevelRootUiHandler - could not get PrefabEditInterface on LevelRootUiHandler construction.");
return;
}
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
if (m_prefabPublicInterface == nullptr)
@@ -14,7 +14,6 @@ namespace AzToolsFramework
{
namespace Prefab
{
class PrefabEditInterface;
class PrefabPublicInterface;
};
@@ -36,7 +35,6 @@ namespace AzToolsFramework
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
private:
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
static constexpr int m_levelRootBorderThickness = 1;
@@ -1,43 +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
*
*/
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzToolsFramework
{
namespace Prefab
{
/*!
* PrefabEditInterface
* Interface to expose the API to Edit Prefabs in the Editor.
*/
class PrefabEditInterface
{
public:
AZ_RTTI(PrefabEditInterface, "{DABB1D43-3760-420E-9F1E-5104F0AFF167}");
/**
* Sets the prefab for the instance owning the entity provided as the prefab being edited.
* @param entityId The entity whose owning prefab should be edited.
*/
virtual void EditOwningPrefab(AZ::EntityId entityId) = 0;
/**
* Queries the Edit Manager to know if the provided entity is part of the prefab currently being edited.
* @param entityId The entity whose prefab editing state we want to query.
* @return True if the prefab owning this entity is being edited, false otherwise.
*/
virtual bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) = 0;
};
} // namespace Prefab
} // namespace AzToolsFramework
@@ -1,46 +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 <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzToolsFramework
{
namespace Prefab
{
PrefabEditManager::PrefabEditManager()
{
m_prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
if (m_prefabPublicInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabPublicInterface on PrefabEditManager construction.");
return;
}
AZ::Interface<PrefabEditInterface>::Register(this);
}
PrefabEditManager::~PrefabEditManager()
{
AZ::Interface<PrefabEditInterface>::Unregister(this);
}
void PrefabEditManager::EditOwningPrefab(AZ::EntityId entityId)
{
m_instanceBeingEdited = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId);
}
bool PrefabEditManager::IsOwningPrefabBeingEdited(AZ::EntityId entityId)
{
AZ::EntityId containerEntity = m_prefabPublicInterface->GetInstanceContainerEntityId(entityId);
return m_instanceBeingEdited == containerEntity;
}
}
}
@@ -1,40 +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
*
*/
#pragma once
#include <AzCore/Component/EntityId.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabEditManager final
: private PrefabEditInterface
{
public:
AZ_CLASS_ALLOCATOR(PrefabEditManager, AZ::SystemAllocator, 0);
PrefabEditManager();
~PrefabEditManager();
private:
// PrefabEditInterface...
void EditOwningPrefab(AZ::EntityId entityId) override;
bool IsOwningPrefabBeingEdited(AZ::EntityId entityId) override;
AZ::EntityId m_instanceBeingEdited;
PrefabPublicInterface* m_prefabPublicInterface;
};
}
}
@@ -22,6 +22,7 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
@@ -57,9 +58,9 @@ namespace AzToolsFramework
{
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr;
PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr;
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr;
const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab";
@@ -102,13 +103,6 @@ namespace AzToolsFramework
return;
}
s_prefabEditInterface = AZ::Interface<PrefabEditInterface>::Get();
if (s_prefabEditInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabEditInterface on PrefabIntegrationManager construction.");
return;
}
s_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
if (s_prefabLoaderInterface == nullptr)
{
@@ -123,6 +117,13 @@ namespace AzToolsFramework
return;
}
s_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
if (s_prefabFocusInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction.");
return;
}
EditorContextMenuBus::Handler::BusConnect();
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
AZ::Interface<PrefabIntegrationInterface>::Register(this);
@@ -224,7 +225,7 @@ namespace AzToolsFramework
// Edit Prefab
if (prefabWipFeaturesEnabled)
{
bool beingEdited = s_prefabEditInterface->IsOwningPrefabBeingEdited(selectedEntity);
bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity);
if (!beingEdited)
{
@@ -428,7 +429,7 @@ namespace AzToolsFramework
void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity)
{
s_prefabEditInterface->EditOwningPrefab(containerEntity);
s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity);
}
void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity)
@@ -17,7 +17,7 @@
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
@@ -28,7 +28,7 @@ namespace AzToolsFramework
{
namespace Prefab
{
class PrefabFocusInterface;
class PrefabLoaderInterface;
//! Structure for saving/retrieving user settings related to prefab workflows.
@@ -80,9 +80,6 @@ namespace AzToolsFramework
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
private:
// Manages the Edit Mode UI for prefabs
PrefabEditManager m_prefabEditManager;
// Used to handle the UI for the level root
LevelRootUiHandler m_levelRootUiHandler;
@@ -135,13 +132,12 @@ namespace AzToolsFramework
AZStd::unique_ptr<QDialog> ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference);
void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog);
static const AZStd::string s_prefabFileExtension;
static EditorEntityUiInterface* s_editorEntityUiInterface;
static PrefabPublicInterface* s_prefabPublicInterface;
static PrefabEditInterface* s_prefabEditInterface;
static PrefabFocusInterface* s_prefabFocusInterface;
static PrefabLoaderInterface* s_prefabLoaderInterface;
static PrefabPublicInterface* s_prefabPublicInterface;
static PrefabSystemComponentInterface* s_prefabSystemComponentInterface;
};
}
@@ -8,7 +8,7 @@
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
@@ -26,21 +26,19 @@ namespace AzToolsFramework
PrefabUiHandler::PrefabUiHandler()
{
m_prefabEditInterface = AZ::Interface<Prefab::PrefabEditInterface>::Get();
if (m_prefabEditInterface == nullptr)
{
AZ_Assert(false, "PrefabUiHandler - could not get PrefabEditInterface on PrefabUiHandler construction.");
return;
}
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
if (m_prefabPublicInterface == nullptr)
{
AZ_Assert(false, "PrefabUiHandler - could not get PrefabPublicInterface on PrefabUiHandler construction.");
return;
}
m_prefabFocusInterface = AZ::Interface<Prefab::PrefabFocusInterface>::Get();
if (m_prefabFocusInterface == nullptr)
{
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction.");
return;
}
}
QString PrefabUiHandler::GenerateItemInfoString(AZ::EntityId entityId) const
@@ -83,7 +81,7 @@ namespace AzToolsFramework
QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const
{
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
{
return QIcon(m_prefabEditIconPath);
}
@@ -105,7 +103,7 @@ namespace AzToolsFramework
const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value<bool>() && index.model()->hasChildren(index);
QColor backgroundColor = m_prefabCapsuleColor;
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
{
backgroundColor = m_prefabCapsuleEditColor;
}
@@ -191,7 +189,7 @@ namespace AzToolsFramework
const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
QColor borderColor = m_prefabCapsuleColor;
if (m_prefabEditInterface->IsOwningPrefabBeingEdited(entityId))
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
{
borderColor = m_prefabCapsuleEditColor;
}
@@ -12,9 +12,10 @@
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabEditInterface;
class PrefabFocusInterface;
class PrefabPublicInterface;
};
@@ -37,7 +38,7 @@ namespace AzToolsFramework
const QModelIndex& descendantIndex) const override;
private:
Prefab::PrefabEditInterface* m_prefabEditInterface = nullptr;
Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr;
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child);
@@ -13,6 +13,7 @@
#include <AzFramework/Viewport/CameraState.h>
#include <AzFramework/Viewport/ViewportScreen.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/API/EditorViewportIconDisplayInterface.h>
#include <AzToolsFramework/ToolsComponents/EditorEntityIconComponentBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
@@ -112,6 +113,17 @@ namespace AzToolsFramework
}
}
EditorHelpers::EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache)
: m_entityDataCache(entityDataCache)
{
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
AZ_Assert(
m_focusModeInterface,
"EditorHelpers - "
"Focus Mode Interface could not be found. "
"Check that it is being correctly initialized.");
}
AZ::EntityId EditorHelpers::HandleMouseInteraction(
const AzFramework::CameraState& cameraState, const ViewportInteraction::MouseInteractionEvent& mouseInteraction)
{
@@ -173,6 +185,12 @@ namespace AzToolsFramework
}
}
// Verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor))
{
return AZ::EntityId();
}
return entityIdUnderCursor;
}
@@ -22,6 +22,7 @@ namespace AzFramework
namespace AzToolsFramework
{
class EditorVisibleEntityDataCache;
class FocusModeInterface;
namespace ViewportInteraction
{
@@ -38,10 +39,7 @@ namespace AzToolsFramework
//! An EditorVisibleEntityDataCache must be passed to EditorHelpers to allow it to
//! efficiently read entity data without resorting to EBus calls.
explicit EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache)
: m_entityDataCache(entityDataCache)
{
}
explicit EditorHelpers(const EditorVisibleEntityDataCache* entityDataCache);
EditorHelpers(const EditorHelpers&) = delete;
EditorHelpers& operator=(const EditorHelpers&) = delete;
~EditorHelpers() = default;
@@ -62,5 +60,6 @@ namespace AzToolsFramework
private:
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers.
const FocusModeInterface* m_focusModeInterface = nullptr;
};
} // namespace AzToolsFramework
@@ -3642,7 +3642,7 @@ namespace AzToolsFramework
}
}
void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& newViewId)
void EditorTransformComponentSelection::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId)
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
@@ -3650,12 +3650,12 @@ namespace AzToolsFramework
// match the editor camera translation/orientation), record the entity id if we have
// a manipulator tracking it (entity id exists in m_entityIdManipulator lookups)
// and remove it when recreating manipulators (see InitializeManipulators)
if (newViewId.IsValid())
if (viewEntityId.IsValid())
{
const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(newViewId);
const auto entityIdLookupIt = m_entityIdManipulators.m_lookups.find(viewEntityId);
if (entityIdLookupIt != m_entityIdManipulators.m_lookups.end())
{
m_editorCameraComponentEntityId = newViewId;
m_editorCameraComponentEntityId = viewEntityId;
RegenerateManipulators();
}
}
@@ -270,7 +270,7 @@ namespace AzToolsFramework
void OnTransformChanged(const AZ::Transform& localTM, const AZ::Transform& worldTM) override;
// Camera::EditorCameraNotificationBus overrides ...
void OnViewportViewEntityChanged(const AZ::EntityId& newViewId) override;
void OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) override;
// EditorContextVisibilityNotificationBus overrides ...
void OnEntityVisibilityChanged(bool visibility) override;
@@ -149,6 +149,9 @@ set(FILES
Entity/SliceEditorEntityOwnershipServiceBus.h
Fingerprinting/TypeFingerprinter.h
Fingerprinting/TypeFingerprinter.cpp
FocusMode/FocusModeInterface.h
FocusMode/FocusModeSystemComponent.h
FocusMode/FocusModeSystemComponent.cpp
Logger/TraceLogger.cpp
Logger/TraceLogger.h
Manipulators/AngularManipulator.cpp
@@ -629,6 +632,9 @@ set(FILES
Prefab/PrefabDomTypes.h
Prefab/PrefabDomUtils.h
Prefab/PrefabDomUtils.cpp
Prefab/PrefabFocusHandler.h
Prefab/PrefabFocusHandler.cpp
Prefab/PrefabFocusInterface.h
Prefab/PrefabIdTypes.h
Prefab/PrefabLoader.h
Prefab/PrefabLoader.cpp
@@ -721,9 +727,6 @@ set(FILES
UI/Layer/LayerUiHandler.cpp
UI/Prefab/LevelRootUiHandler.h
UI/Prefab/LevelRootUiHandler.cpp
UI/Prefab/PrefabEditInterface.h
UI/Prefab/PrefabEditManager.h
UI/Prefab/PrefabEditManager.cpp
UI/Prefab/PrefabIntegrationBus.h
UI/Prefab/PrefabIntegrationManager.h
UI/Prefab/PrefabIntegrationManager.cpp
@@ -0,0 +1,128 @@
/*
* 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 <AzTest/AzTest.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
namespace AzToolsFramework
{
class EditorFocusModeTests
: public ::testing::Test
{
protected:
void SetUp() override
{
m_app.Start(m_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
// 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());
}
TEST_F(EditorFocusModeTests, EditorFocusModeTests_IsInFocusSubTree)
{
FocusModeInterface* focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
EXPECT_TRUE(focusModeInterface != nullptr);
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);
focusModeInterface->SetFocusRoot(m_entityMap["streetId"]);
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);
focusModeInterface->SetFocusRoot(m_entityMap["carId"]);
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);
focusModeInterface->SetFocusRoot(m_entityMap["passengerId2"]);
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);
focusModeInterface->ClearFocusRoot();
}
}
@@ -0,0 +1,173 @@
/*
* 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/Component/EntityId.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <Prefab/PrefabTestFixture.h>
namespace UnitTest
{
class PrefabFocusTests
: public PrefabTestFixture
{
protected:
void GenerateTestHierarchy()
{
/*
* City (Prefab Container)
* |_ City
* |_ Street (Prefab Container)
* |_ Car (Prefab Container)
* | |_ Passenger
* |_ SportsCar (Prefab Container)
* |_ Passenger
*/
m_entityMap["passenger1"] = CreateEntity("Passenger1");
m_entityMap["passenger2"] = CreateEntity("Passenger2");
m_entityMap["city"] = CreateEntity("City");
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequests::HandleEntitiesAdded,
AzToolsFramework::EntityList{ m_entityMap["passenger1"], m_entityMap["passenger2"], m_entityMap["city"] });
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> carInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger1"] }, {}, "test/car");
ASSERT_TRUE(carInstance);
m_instanceMap["car"] = carInstance.get();
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> sportsCarInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["passenger2"] }, {}, "test/sportsCar");
ASSERT_TRUE(sportsCarInstance);
m_instanceMap["sportsCar"] = sportsCarInstance.get();
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> streetInstance =
m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(carInstance), AZStd::move(sportsCarInstance) ), "test/street");
ASSERT_TRUE(streetInstance);
m_instanceMap["street"] = streetInstance.get();
m_rootInstance =
m_prefabSystemComponent->CreatePrefab({ m_entityMap["city"] }, MakeInstanceList(AZStd::move(streetInstance)), "test/city");
ASSERT_TRUE(m_rootInstance);
m_instanceMap["city"] = m_rootInstance.get();
}
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;
};
TEST_F(PrefabFocusTests, PrefabFocus_FocusOnOwningPrefab)
{
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());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["city"]);
}
// 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());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["city"]);
}
// 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());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["car"]);
}
// 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());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), m_instanceMap["car"]);
}
// Verify FocusOnOwningPrefab points to the root prefab when the focus is cleared.
{
AzToolsFramework::PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
AzToolsFramework::Prefab::InstanceOptionalReference rootPrefabInstance =
prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
EXPECT_TRUE(rootPrefabInstance.has_value());
prefabFocusInterface->FocusOnOwningPrefab(AZ::EntityId());
EXPECT_EQ(prefabFocusInterface->GetFocusedPrefabTemplateId(), rootPrefabInstance->get().GetTemplateId());
auto instance = prefabFocusInterface->GetFocusedPrefabInstance();
EXPECT_TRUE(instance.has_value());
EXPECT_EQ(&instance->get(), &rootPrefabInstance->get());
}
m_rootInstance.release();
}
TEST_F(PrefabFocusTests, PrefabFocus_IsOwningPrefabBeingFocused)
{
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());
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_instanceMap["city"]->GetContainerEntityId()));
EXPECT_TRUE(prefabFocusInterface->IsOwningPrefabBeingFocused(m_entityMap["city"]->GetId()));
}
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (ancestors/descendants)
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["street"]->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()));
}
// Verify IsOwningPrefabBeingFocused returns false for all entities not in a focused prefab (siblings)
{
prefabFocusInterface->FocusOnOwningPrefab(m_instanceMap["sportsCar"]->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()));
}
m_rootInstance.release();
}
}
@@ -34,6 +34,7 @@ set(FILES
EntityTestbed.h
FileFunc.cpp
FingerprintingTests.cpp
FocusMode/EditorFocusModeTests.cpp
GenericComponentWrapperTest.cpp
InstanceDataHierarchy.cpp
IntegerPrimtitiveTestConfig.h
@@ -50,6 +51,7 @@ set(FILES
Prefab/Benchmark/PrefabLoadBenchmarks.cpp
Prefab/Benchmark/PrefabUpdateInstancesBenchmarks.cpp
Prefab/Benchmark/SpawnableCreateBenchmarks.cpp
Prefab/PrefabFocus/PrefabFocusTests.cpp
Prefab/MockPrefabFileIOActionValidator.cpp
Prefab/MockPrefabFileIOActionValidator.h
Prefab/PrefabDuplicateTests.cpp