Merge remote-tracking branch 'upstream/development' into nvsickle/OutlinerDuplicateEntryFixes

This commit is contained in:
nvsickle
2021-10-13 13:24:24 -07:00
1722 changed files with 154722 additions and 22915 deletions
@@ -0,0 +1,29 @@
/*
* 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/Outcome/Outcome.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
namespace AzToolsFramework
{
//! The AZ::Interface for component mode collection queries.
class ComponentModeCollectionInterface
{
public:
AZ_RTTI(ComponentModeCollectionInterface, "{DFAA4450-BBCD-47C0-9B91-FEA2DBD9B152}");
virtual ~ComponentModeCollectionInterface() = default;
//! Retrieves the list of all Component types (usually one).
//! @note If called outside of component mode, an empty vector will be returned.
virtual AZStd::vector<AZ::Uuid> GetComponentTypes() const = 0;
};
} // namespace AzToolsFramework
@@ -47,14 +47,6 @@ namespace AzToolsFramework
//! Retrieve the absolute path for the Asset Database Location
virtual bool GetAbsoluteAssetDatabaseLocation(AZStd::string& /*result*/) { return false; }
//! Retrieve the absolute folder path to the current game's source assets (the ones that go into source control)
//! This may include the current mod path, if a mod is being edited by the editor
virtual const char* GetAbsoluteDevGameFolderPath() = 0;
//! Retrieve the absolute folder path to the current developer root ('dev'), which contains source artifacts
//! and is generally checked into source control.
virtual const char* GetAbsoluteDevRootFolderPath() = 0;
/// Convert a full source path like "c:\\dev\\gamename\\blah\\test.tga" into a relative product path.
/// asset paths never mention their alias and are relative to the asset cache root
@@ -764,12 +764,6 @@ namespace AzToolsFramework
//! Spawn asset browser for the appropriate asset types.
virtual void BrowseForAssets(AssetBrowser::AssetSelectionModel& /*selection*/) = 0;
/// Allow interception of selection / left-mouse clicks in ObjectMode, for customizing selection behavior.
virtual void HandleObjectModeSelection(const AZ::Vector2& /*point*/, int /*flags*/, bool& /*handled*/) {}
/// Allow interception of cursor, for customizing selection behavior.
virtual void UpdateObjectModeCursor(AZ::u32& /*cursorId*/, AZStd::string& /*cursorStr*/) {}
/// Creates editor-side representation of an underlying entity.
virtual void CreateEditorRepresentation(AZ::Entity* /*entity*/) { }
@@ -22,22 +22,22 @@ namespace AzToolsFramework
virtual ~ViewportEditorModeTrackerInterface() = default;
//! Activates the specified editor mode for the specified viewport.
//! Activates the specified editor mode for the specified viewport editor mode tracker.
virtual AZ::Outcome<void, AZStd::string> ActivateMode(
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) = 0;
const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo, ViewportEditorMode mode) = 0;
//! Deactivates the specified editor mode for the specified viewport.
//! Deactivates the specified editor mode for the specified viewport editor mode tracker.
virtual AZ::Outcome<void, AZStd::string> DeactivateMode(
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode) = 0;
const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo, ViewportEditorMode mode) = 0;
//! Attempts to retrieve the editor mode state for the specified viewport, otherwise returns nullptr.
virtual const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeInfo& viewportEditorModeInfo) const = 0;
//! Attempts to retrieve the editor mode state for the specified viewport editor mode tracker, otherwise returns nullptr.
virtual const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo) const = 0;
//! Returns the number of viewports currently being tracked.
//! Returns the number of viewport editor mode trackers.
virtual size_t GetTrackedViewportCount() const = 0;
//! Returns true if the specified viewport is being tracked, otherwise false.
virtual bool IsViewportModeTracked(const ViewportEditorModeInfo& viewportEditorModeInfo) const = 0;
//! Returns true if viewport editor modes are being tracked for the specified od, otherwise false.
virtual bool IsViewportModeTracked(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo) const = 0;
};
} // namespace AzToolsFramework
@@ -0,0 +1,27 @@
/*
* 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/RTTI/BehaviorContext.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
namespace AzToolsFramework
{
void ViewportEditorModeNotifications::Reflect(AZ::ReflectContext* context)
{
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<ViewportEditorModeNotificationsBus>("ViewportEditorModeNotificationsBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "editor")
->Event("OnEditorModeActivated", &ViewportEditorModeNotifications::OnEditorModeActivated)
->Event("OnEditorModeDeactivated", &ViewportEditorModeNotifications::OnEditorModeDeactivated)
;
}
}
} // namespace AzToolsFramework
@@ -9,7 +9,7 @@
#pragma once
#include <AzCore/EBus/Event.h>
#include <AzFramework/Viewport/ViewportId.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzToolsFramework/ViewportUi/ViewportUiRequestBus.h>
namespace AzToolsFramework
@@ -23,17 +23,19 @@ namespace AzToolsFramework
Pick
};
//! Viewport identifier and other relevant viewport data.
struct ViewportEditorModeInfo
//! Viewport editor mode tracker identifier and other relevant data.
struct ViewportEditorModeTrackerInfo
{
using IdType = AzFramework::ViewportId;
IdType m_id = ViewportUi::DefaultViewportId; //!< The unique identifier for a given viewport.
using IdType = AzFramework::EntityContextId;
IdType m_id = AzFramework::EntityContextId::CreateNull(); //!< The unique identifier for a given viewport editor mode tracker.
};
//! Interface for the editor modes of a given viewport.
class ViewportEditorModesInterface
{
public:
AZ_RTTI(ViewportEditorModesInterface, "{2421496C-4A46-41C9-8AEF-AE2B6E43E6CF}");
virtual ~ViewportEditorModesInterface() = default;
//! Returns true if the specified editor mode is active, otherwise false.
@@ -49,9 +51,12 @@ namespace AzToolsFramework
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = ViewportEditorModeInfo::IdType;
using BusIdType = ViewportEditorModeTrackerInfo::IdType;
//////////////////////////////////////////////////////////////////////////
AZ_RTTI(ViewportEditorModeNotifications, "{9469DE39-6C21-423C-94FA-EF3A9616B14F}", AZ::EBusTraits);
static void Reflect(AZ::ReflectContext* context);
//! Notifies subscribers of the a given viewport to the activation of the specified editor mode.
virtual void OnEditorModeActivated([[maybe_unused]] const ViewportEditorModesInterface& editorModeState, [[maybe_unused]] ViewportEditorMode mode)
{
@@ -27,6 +27,7 @@
#include <AzToolsFramework/ToolsComponents/EditorAssetMimeDataContainer.h>
#include <AzToolsFramework/ToolsComponents/ComponentAssetMimeDataContainer.h>
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h>
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
@@ -68,6 +69,7 @@
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiSystemComponent.h>
#include <AzToolsFramework/Undo/UndoCacheInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <Entity/EntityUtilityComponent.h>
#include <QtWidgets/QMessageBox>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer<QFileInfoPrivate>' needs to have dll-interface to be used by clients of class 'QFileInfo'
@@ -251,6 +253,7 @@ namespace AzToolsFramework
azrtti_typeid<EditorEntityContextComponent>(),
azrtti_typeid<Components::EditorEntityUiSystemComponent>(),
azrtti_typeid<FocusModeSystemComponent>(),
azrtti_typeid<ContainerEntitySystemComponent>(),
azrtti_typeid<SliceMetadataEntityContextComponent>(),
azrtti_typeid<Prefab::PrefabSystemComponent>(),
azrtti_typeid<EditorEntityFixupComponent>(),
@@ -269,7 +272,8 @@ namespace AzToolsFramework
azrtti_typeid<AzToolsFramework::EditorInteractionSystemComponent>(),
azrtti_typeid<Components::EditorEntitySearchComponent>(),
azrtti_typeid<Components::EditorIntersectorComponent>(),
azrtti_typeid<AzToolsFramework::SliceRequestComponent>()
azrtti_typeid<AzToolsFramework::SliceRequestComponent>(),
azrtti_typeid<AzToolsFramework::EntityUtilityComponent>()
});
return components;
@@ -21,7 +21,7 @@
namespace AzToolsFramework
{
constexpr const char s_traceName[] = "ArchiveComponent";
[[maybe_unused]] 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;
@@ -354,26 +354,6 @@ namespace AzToolsFramework
}
}
const char* AssetSystemComponent::GetAbsoluteDevGameFolderPath()
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (fileIO)
{
return fileIO->GetAlias("@devassets@");
}
return "";
}
const char* AssetSystemComponent::GetAbsoluteDevRootFolderPath()
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
if (fileIO)
{
return fileIO->GetAlias("@devroot@");
}
return "";
}
void AssetSystemComponent::OnSystemTick()
{
AssetSystemBus::ExecuteQueuedEvents();
@@ -56,8 +56,6 @@ namespace AzToolsFramework
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::AssetSystemRequestBus::Handler overrides
bool GetAbsoluteAssetDatabaseLocation(AZStd::string& result) override;
const char* GetAbsoluteDevGameFolderPath() override;
const char* GetAbsoluteDevRootFolderPath() override;
bool GetRelativeProductPathFromFullSourceOrProductPath(const AZStd::string& fullPath, AZStd::string& outputPath) override;
bool GenerateRelativeSourcePath(
const AZStd::string& sourcePath, AZStd::string& outputPath, AZStd::string& watchFolder) override;
@@ -137,7 +137,7 @@ namespace AzToolsFramework
AssetEditorWidgetUserSettings::AssetEditorWidgetUserSettings()
{
char assetRoot[AZ_MAX_PATH_LEN] = { 0 };
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", assetRoot, AZ_MAX_PATH_LEN);
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", assetRoot, AZ_MAX_PATH_LEN);
m_lastSavePath = assetRoot;
}
@@ -410,7 +410,7 @@ namespace AzToolsFramework
filter.append(ext);
if (i < n - 1)
{
filter.append(", ");
filter.append(" ");
}
}
filter.append(")");
@@ -16,6 +16,7 @@
#include <AzToolsFramework/AssetBundle/AssetBundleComponent.h>
#include <AzToolsFramework/Component/EditorComponentAPIComponent.h>
#include <AzToolsFramework/Component/EditorLevelComponentAPIComponent.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h>
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
#include <AzToolsFramework/Entity/EditorEntityFixupComponent.h>
@@ -52,6 +53,7 @@
#include <AzToolsFramework/Thumbnails/ThumbnailerNullComponent.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h>
#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
AZ_DEFINE_BUDGET(AzToolsFramework);
@@ -70,6 +72,8 @@ namespace AzToolsFramework
Components::EditorSelectionAccentSystemComponent::CreateDescriptor(),
EditorEntityContextComponent::CreateDescriptor(),
EditorEntityFixupComponent::CreateDescriptor(),
EntityUtilityComponent::CreateDescriptor(),
ContainerEntitySystemComponent::CreateDescriptor(),
FocusModeSystemComponent::CreateDescriptor(),
SliceMetadataEntityContextComponent::CreateDescriptor(),
SliceRequestComponent::CreateDescriptor(),
@@ -203,6 +203,12 @@ namespace AzToolsFramework
}
}
AZStd::vector<AZ::Uuid> ComponentModeCollection::GetComponentTypes() const
{
// If in component mode, return the active component types, otherwise return an empty vector
return InComponentMode() ? m_activeComponentTypes : AZStd::vector<AZ::Uuid>{};
}
void ComponentModeCollection::BeginComponentMode()
{
m_selectedComponentModeIndex = 0;
@@ -211,14 +217,7 @@ namespace AzToolsFramework
// notify listeners the editor has entered ComponentMode - listeners may
// wish to modify state to indicate this (e.g. appearance, functionality etc.)
EditorComponentModeNotificationBus::Event(
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);
m_viewportEditorModeTracker->ActivateMode({ GetEntityContextId() }, ViewportEditorMode::Component);
// enable actions for the first/primary ComponentMode
// note: if multiple ComponentModes are activated at the same time, actions
@@ -288,15 +287,7 @@ namespace AzToolsFramework
// notify listeners the editor has left ComponentMode - listeners may
// wish to modify state to indicate this (e.g. appearance, functionality etc.)
EditorComponentModeNotificationBus::Event(
GetEntityContextId(),
&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);
m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Component);
// clear stored modes and builders for this ComponentMode
// TLDR: avoid 'use after free' error
@@ -9,6 +9,7 @@
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/API/ComponentModeCollectionInterface.h>
#include <AzToolsFramework/ComponentMode/ComponentModeViewportUi.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
@@ -21,6 +22,7 @@ namespace AzToolsFramework
{
/// Manages all individual ComponentModes for a single instance of Editor wide ComponentMode.
class ComponentModeCollection
: public ComponentModeCollectionInterface
{
public:
AZ_CLASS_ALLOCATOR_DECL
@@ -89,6 +91,9 @@ namespace AzToolsFramework
/// Called once each time a ComponentMode is added.
void PopulateViewportUi();
// ComponentModeCollectionInterface overrides ...
AZStd::vector<AZ::Uuid> GetComponentTypes() const override;
private:
// Internal helper used by Select[|Prev|Next]ActiveComponentMode
bool ActiveComponentModeChanged(const AZ::Uuid& previousComponentType);
@@ -24,18 +24,8 @@ namespace AzToolsFramework
: public EditorComponentModeNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
AZ_EBUS_BEHAVIOR_BINDER(EditorComponentModeNotificationBusHandler, "{AD2F4204-0913-4FC9-9A10-492538F60C70}", AZ::SystemAllocator,
EnteredComponentMode, LeftComponentMode, ActiveComponentModeChanged);
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) override
{
Call(FN_EnteredComponentMode, componentTypes);
}
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) override
{
Call(FN_LeftComponentMode, componentTypes);
}
AZ_EBUS_BEHAVIOR_BINDER(
EditorComponentModeNotificationBusHandler, "{AD2F4204-0913-4FC9-9A10-492538F60C70}", AZ::SystemAllocator, ActiveComponentModeChanged);
void ActiveComponentModeChanged(const AZ::Uuid& componentType) override
{
@@ -171,8 +161,6 @@ namespace AzToolsFramework
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "editor")
->Handler<Internal::EditorComponentModeNotificationBusHandler>()
->Event("EnteredComponentMode", &EditorComponentModeNotifications::EnteredComponentMode)
->Event("LeftComponentMode", &EditorComponentModeNotifications::LeftComponentMode)
->Event("ActiveComponentModeChanged", &EditorComponentModeNotifications::ActiveComponentModeChanged)
;
}
@@ -238,12 +238,6 @@ namespace AzToolsFramework
using BusIdType = AzFramework::EntityContextId;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
/// Called when Editor enters ComponentMode - pass the list of all Component types (usually one).
virtual void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) = 0;
/// Called when Editor leaves ComponentMode - pass the list of all Component types (usually one).
virtual void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) = 0;
/// Called when Tab is pressed to cycle the 'selected' ComponentMode (which shortcuts/actions are active).
/// Also called when directly selecting a Component in the EntityOutliner.
virtual void ActiveComponentModeChanged(const AZ::Uuid& /*componentType*/) {}
@@ -255,42 +249,6 @@ namespace AzToolsFramework
/// Type to inherit to implement EditorComponentModeNotifications.
using EditorComponentModeNotificationBus = AZ::EBus<EditorComponentModeNotifications>;
/// Helper for EditorComponentModeNotifications to be used
/// as a member instead of inheriting from EBus directly.
class EditorComponentModeNotificationBusImpl
: public EditorComponentModeNotificationBus::Handler
{
public:
/// Set the function to be called when entering ComponentMode.
void SetEnteredComponentModeFunc(
const AZStd::function<void(const AZStd::vector<AZ::Uuid>&)>& enteredComponentModeFunc)
{
m_enteredComponentModeFunc = enteredComponentModeFunc;
}
/// Set the function to be called when leaving ComponentMode.
void SetLeftComponentModeFunc(
const AZStd::function<void(const AZStd::vector<AZ::Uuid>&)>& leftComponentModeFunc)
{
m_leftComponentModeFunc = leftComponentModeFunc;
}
private:
// EditorComponentModeNotificationBus
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override
{
m_enteredComponentModeFunc(componentModeTypes);
}
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override
{
m_leftComponentModeFunc(componentModeTypes);
}
AZStd::function<void(const AZStd::vector<AZ::Uuid>&)> m_enteredComponentModeFunc; ///< Function to call when entering ComponentMode.
AZStd::function<void(const AZStd::vector<AZ::Uuid>&)> m_leftComponentModeFunc; ///< Function to call when leaving ComponentMode.
};
/// Helper to answer if the Editor is in ComponentMode or not.
inline bool InComponentMode()
{
@@ -0,0 +1,73 @@
/*
* 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 <AzFramework/Entity/EntityContextBus.h>
namespace AzToolsFramework
{
//! Outcome object that returns an error message in case of failure to allow caller to handle internal errors.
using ContainerEntityOperationResult = AZ::Outcome<void, AZStd::string>;
//! ContainerEntityInterface
//! An entity registered as Container is just like a regular entity when open. If its state is changed
//! to closed, all descendants of the entity will be treated as part of the entity itself. Selecting any
//! descendant will result in the container being selected, and descendants will be hidden until the
//! container is opened.
class ContainerEntityInterface
{
public:
AZ_RTTI(ContainerEntityInterface, "{0A877C3A-726C-4FD2-BAFE-A2B9F1DE78E4}");
//! Registers the entity as a container. The container will be closed by default.
//! @param entityId The entityId that will be registered as a container.
virtual ContainerEntityOperationResult RegisterEntityAsContainer(AZ::EntityId entityId) = 0;
//! Unregisters the entity as a container.
//! The system will retain the closed state in case the entity is registered again later, but
//! if queried the entity will no longer behave as a container.
//! @param entityId The entityId that will be unregistered as a container.
virtual ContainerEntityOperationResult UnregisterEntityAsContainer(AZ::EntityId entityId) = 0;
//! Returns whether the entity id provided is registered as a container.
virtual bool IsContainer(AZ::EntityId entityId) const = 0;
//! Sets the open state of the container entity provided.
//! @param entityId The entityId whose open state will be set.
//! @param open True if the container should be opened, false if it should be closed.
//! @return An error message if the operation was invalid, success otherwise.
virtual ContainerEntityOperationResult SetContainerOpen(AZ::EntityId entityId, bool open) = 0;
//! If the entity id provided is registered as a container, it returns whether it's open.
//! @note the default value for non-containers is true, so this function can be called without
//! verifying whether the entityId is registered as a container beforehand, since the container's
//! open behavior is exactly the same as the one of a regular entity.
//! @return False if the entityId is registered as a container, and its state is closed. True otherwise.
virtual bool IsContainerOpen(AZ::EntityId entityId) const = 0;
//! Detects if one of the ancestors of entityId is a closed container entity.
//! @return The highest closed entity container id if any, or entityId otherwise.
virtual AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const = 0;
//! Clears all open state information for Container Entities for the EntityContextId provided.
//! Used when context is switched, for example in the case of a new root prefab being loaded
//! in place of an old one.
//! @note Clear is meant to be called when no container is registered for the context provided.
//! @return An error message if any container was registered for the context, success otherwise.
virtual ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) = 0;
//! Returns true if one of the ancestors of entityId is a closed container entity.
virtual bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const = 0;
};
} // namespace AzToolsFramework
@@ -0,0 +1,41 @@
/*
* 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 changes of state for Container Entities.
class ContainerEntityNotifications
: 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 a container entity status changes.
//! @param entityId The entity whose status has changed.
//! @param open The open state the container was changed to.
virtual void OnContainerEntityStatusChanged([[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] bool open) {}
protected:
~ContainerEntityNotifications() = default;
};
using ContainerEntityNotificationBus = AZ::EBus<ContainerEntityNotifications>;
} // namespace AzToolsFramework
@@ -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 <AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h>
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityNotificationBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
namespace AzToolsFramework
{
void ContainerEntitySystemComponent::Activate()
{
AZ::Interface<ContainerEntityInterface>::Register(this);
EditorEntityContextNotificationBus::Handler::BusConnect();
}
void ContainerEntitySystemComponent::Deactivate()
{
EditorEntityContextNotificationBus::Handler::BusDisconnect();
AZ::Interface<ContainerEntityInterface>::Unregister(this);
}
void ContainerEntitySystemComponent::Reflect([[maybe_unused]] AZ::ReflectContext* context)
{
}
void ContainerEntitySystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC_CE("ContainerEntityService"));
}
ContainerEntityOperationResult ContainerEntitySystemComponent::RegisterEntityAsContainer(AZ::EntityId entityId)
{
if (IsContainer(entityId))
{
return AZ::Failure(AZStd::string(
"ContainerEntitySystemComponent error - trying to register entity as container twice."));
}
m_containers.insert(entityId);
return AZ::Success();
}
ContainerEntityOperationResult ContainerEntitySystemComponent::UnregisterEntityAsContainer(AZ::EntityId entityId)
{
if (!IsContainer(entityId))
{
return AZ::Failure(AZStd::string(
"ContainerEntitySystemComponent error - trying to unregister entity that is not a container."));
}
m_containers.erase(entityId);
return AZ::Success();
}
bool ContainerEntitySystemComponent::IsContainer(AZ::EntityId entityId) const
{
return m_containers.contains(entityId);
}
ContainerEntityOperationResult ContainerEntitySystemComponent::SetContainerOpen(AZ::EntityId entityId, bool open)
{
if (!IsContainer(entityId))
{
return AZ::Failure(AZStd::string(
"ContainerEntitySystemComponent error - cannot set open state of entity that was not registered as container."));
}
if(open)
{
m_openContainers.insert(entityId);
}
else
{
m_openContainers.erase(entityId);
}
ContainerEntityNotificationBus::Broadcast(&ContainerEntityNotificationBus::Events::OnContainerEntityStatusChanged, entityId, open);
return AZ::Success();
}
bool ContainerEntitySystemComponent::IsContainerOpen(AZ::EntityId entityId) const
{
// Non-container entities behave the same as open containers. This saves the caller an additional check.
if(!m_containers.contains(entityId))
{
return true;
}
// If the entity is a container, return its state.
return m_openContainers.contains(entityId);
}
AZ::EntityId ContainerEntitySystemComponent::FindHighestSelectableEntity(AZ::EntityId entityId) const
{
if (!entityId.IsValid())
{
return entityId;
}
// Return the highest closed container, or the entity if none is found.
AZ::EntityId highestSelectableEntityId = entityId;
// Skip the queried entity, as we only want to check its ancestors.
AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
// Go up the hierarchy until you hit the root
while (entityId.IsValid())
{
if (!IsContainerOpen(entityId))
{
// If one of the ancestors is a container and it's closed, keep track of its id.
// We only return of the higher closed container in the hierarchy.
highestSelectableEntityId = entityId;
}
AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
}
return highestSelectableEntityId;
}
void ContainerEntitySystemComponent::OnEntityStreamLoadSuccess()
{
// We don't yet support multiple entity contexts, so just use the default.
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
Clear(editorEntityContextId);
}
ContainerEntityOperationResult ContainerEntitySystemComponent::Clear(AzFramework::EntityContextId entityContextId)
{
// We don't yet support multiple entity contexts, so only clear the default.
auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
if (entityContextId != editorEntityContextId)
{
return AZ::Failure(AZStd::string(
"Error in ContainerEntitySystemComponent::Clear - cannot clear non-default Entity Context!"));
}
if (!m_containers.empty())
{
return AZ::Failure(AZStd::string(
"Error in ContainerEntitySystemComponent::Clear - cannot clear container states if entities are still registered!"));
}
m_openContainers.clear();
return AZ::Success();
}
bool ContainerEntitySystemComponent::IsUnderClosedContainerEntity(AZ::EntityId entityId) const
{
if (!entityId.IsValid())
{
return false;
}
// Skip the queried entity, as we only want to check its ancestors.
AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
// Go up the hierarchy until you hit the root.
while (entityId.IsValid())
{
if (!IsContainerOpen(entityId))
{
// One of the ancestors is a container and it's closed.
return true;
}
AZ::EntityId parentId;
AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
if (parentId == entityId)
{
// In some circumstances, querying a root level entity with GetParentId will return
// the entity itself instead of an invalid entityId.
break;
}
entityId = parentId;
}
// All ancestors are either regular entities or open containers.
return false;
}
} // namespace AzToolsFramework
@@ -0,0 +1,61 @@
/*
* 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/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
namespace AzToolsFramework
{
//! System Component to track Container Entity registration and open state.
//! An entity registered as Container is just like a regular entity when open. If its state is changed
//! to closed, all descendants of the entity will be treated as part of the entity itself. Selecting any
//! descendant will result in the container being selected, and descendants will be hidden until the
//! container is opened.
class ContainerEntitySystemComponent final
: public AZ::Component
, private ContainerEntityInterface
, private EditorEntityContextNotificationBus::Handler
{
public:
AZ_COMPONENT(ContainerEntitySystemComponent, "{74349759-B36B-44A6-B89F-F45D7111DD11}");
ContainerEntitySystemComponent() = default;
virtual ~ContainerEntitySystemComponent() = default;
// AZ::Component overrides ...
void Activate() override;
void Deactivate() override;
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
// ContainerEntityInterface overrides ...
ContainerEntityOperationResult RegisterEntityAsContainer(AZ::EntityId entityId) override;
ContainerEntityOperationResult UnregisterEntityAsContainer(AZ::EntityId entityId) override;
bool IsContainer(AZ::EntityId entityId) const override;
ContainerEntityOperationResult SetContainerOpen(AZ::EntityId entityId, bool open) override;
bool IsContainerOpen(AZ::EntityId entityId) const override;
AZ::EntityId FindHighestSelectableEntity(AZ::EntityId entityId) const override;
ContainerEntityOperationResult Clear(AzFramework::EntityContextId entityContextId) override;
bool IsUnderClosedContainerEntity(AZ::EntityId entityId) const override;
// EditorEntityContextNotificationBus overrides ...
void OnEntityStreamLoadSuccess() override;
private:
AZStd::unordered_set<AZ::EntityId> m_containers; //!< All entities in this set are containers.
AZStd::unordered_set<AZ::EntityId> m_openContainers; //!< All entities in this set are open containers.
};
} // namespace AzToolsFramework
@@ -39,7 +39,7 @@ namespace AzToolsFramework
// the TraceContextLogFormatter.
//
// Usage example:
// const char* gameFolder = m_context.pRC->GetSystemEnvironment()->pFileIO->GetAlias("@devassets@");
// const char* gameFolder = m_context.pRC->GetSystemEnvironment()->pFileIO->GetAlias("@projectroot@");
// AZ_TraceContext("Game folder", gameFolder);
//
// for (int i=0; i<subMeshCount; ++i)
@@ -16,9 +16,11 @@
#include <AzCore/RTTI/AttributeReader.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Commands/EntityStateCommand.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/Slice/SliceMetadataEntityContextBus.h>
#include <AzToolsFramework/Slice/SliceUtilities.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
@@ -588,15 +590,40 @@ namespace AzToolsFramework
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
// Detect if the Entity is Visible
bool visible = false;
EditorEntityInfoRequestBus::EventResult(
visible, entityId, &EditorEntityInfoRequestBus::Events::IsVisible);
bool locked = false;
EditorEntityInfoRequestBus::EventResult(
locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked);
if (!visible)
{
return false;
}
return visible && !locked;
// Detect if the Entity is Locked
bool locked = false;
EditorEntityInfoRequestBus::EventResult(locked, entityId, &EditorEntityInfoRequestBus::Events::IsLocked);
if (locked)
{
return false;
}
// Detect if the Entity is part of the Editor Focus
if (auto focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
!focusModeInterface->IsInFocusSubTree(entityId))
{
return false;
}
// Detect if the Entity is a descendant of a closed container
if (auto containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
containerEntityInterface->IsUnderClosedContainerEntity(entityId))
{
return false;
}
return true;
}
static void SetEntityLockStateRecursively(
@@ -0,0 +1,351 @@
/*
* 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 <sstream>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/JsonSerializationSettings.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzFramework/Entity/EntityContext.h>
#include <AzFramework/FileFunc/FileFunc.h>
#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
#include <Entity/EditorEntityContextBus.h>
#include <rapidjson/document.h>
namespace AzToolsFramework
{
void ComponentDetails::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<ComponentDetails>()
->Field("TypeInfo", &ComponentDetails::m_typeInfo)
->Field("BaseClasses", &ComponentDetails::m_baseClasses);
serializeContext->RegisterGenericType<AZStd::vector<ComponentDetails>>();
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<ComponentDetails>()
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "entity")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Property("TypeInfo", BehaviorValueProperty(&ComponentDetails::m_typeInfo))
->Property("BaseClasses", BehaviorValueProperty(&ComponentDetails::m_baseClasses))
->Method("__repr__", [](const ComponentDetails& obj)
{
std::ostringstream result;
bool first = true;
for (const auto& baseClass : obj.m_baseClasses)
{
if (!first)
{
result << ", ";
}
first = false;
result << baseClass.c_str();
}
return AZStd::string::format("%s, Base Classes: <%s>", obj.m_typeInfo.c_str(), result.str().c_str());
})
->Attribute(AZ::Script::Attributes::Operator, AZ::Script::Attributes::OperatorType::ToString);
}
}
AZ::EntityId EntityUtilityComponent::CreateEditorReadyEntity(const AZStd::string& entityName)
{
auto* newEntity = m_entityContext->CreateEntity(entityName.c_str());
if (!newEntity)
{
AZ_Error("EditorEntityUtility", false, "Failed to create new entity %s", entityName.c_str());
return AZ::EntityId();
}
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
&AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *newEntity);
newEntity->Init();
auto newEntityId = newEntity->GetId();
m_createdEntities.emplace_back(newEntityId);
return newEntityId;
}
AZ::TypeId GetComponentTypeIdFromName(const AZStd::string& typeName)
{
// Try to create a TypeId first. We won't show any warnings if this fails as the input might be a class name instead
AZ::TypeId typeId = AZ::TypeId::CreateStringPermissive(typeName.data());
// If the typeId is null, try a lookup by class name
if (typeId.IsNull())
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
auto typeNameCrc = AZ::Crc32(typeName.data());
auto typeUuidList = serializeContext->FindClassId(typeNameCrc);
// TypeId is invalid or class name is invalid
if (typeUuidList.empty())
{
AZ_Error("EntityUtilityComponent", false, "Provided type %s is either an invalid TypeId or does not match any class names", typeName.c_str());
return AZ::TypeId::CreateNull();
}
typeId = typeUuidList[0];
}
return typeId;
}
AZ::Component* FindComponentHelper(AZ::EntityId entityId, const AZ::TypeId& typeId, AZ::ComponentId componentId, bool createComponent = false)
{
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
if (!entity)
{
AZ_Error("EntityUtilityComponent", false, "Invalid entityId %s", entityId.ToString().c_str());
return nullptr;
}
AZ::Component* component = nullptr;
if (componentId != AZ::InvalidComponentId)
{
component = entity->FindComponent(componentId);
}
else
{
component = entity->FindComponent(typeId);
}
if (!component && createComponent)
{
component = entity->CreateComponent(typeId);
}
if (!component)
{
AZ_Error(
"EntityUtilityComponent", false, "Failed to find component (%s) on entity %s (%s)",
componentId != AZ::InvalidComponentId ? AZStd::to_string(componentId).c_str()
: typeId.ToString<AZStd::string>().c_str(),
entityId.ToString().c_str(),
entity->GetName().c_str());
return nullptr;
}
return component;
}
AzFramework::BehaviorComponentId EntityUtilityComponent::GetOrAddComponentByTypeName(AZ::EntityId entityId, const AZStd::string& typeName)
{
AZ::TypeId typeId = GetComponentTypeIdFromName(typeName);
if (typeId.IsNull())
{
return AzFramework::BehaviorComponentId(AZ::InvalidComponentId);
}
AZ::Component* component = FindComponentHelper(entityId, typeId, AZ::InvalidComponentId, true);
return component ? AzFramework::BehaviorComponentId(component->GetId()) :
AzFramework::BehaviorComponentId(AZ::InvalidComponentId);
}
bool EntityUtilityComponent::UpdateComponentForEntity(AZ::EntityId entityId, AzFramework::BehaviorComponentId componentId, const AZStd::string& json)
{
if (!componentId.IsValid())
{
AZ_Error("EntityUtilityComponent", false, "Invalid componentId passed to UpdateComponentForEntity");
return false;
}
AZ::Component* component = FindComponentHelper(entityId, AZ::TypeId::CreateNull(), componentId);
if (!component)
{
return false;
}
using namespace AZ::JsonSerializationResult;
AZ::JsonDeserializerSettings settings = AZ::JsonDeserializerSettings{};
settings.m_reporting = []([[maybe_unused]] AZStd::string_view message, ResultCode result, AZStd::string_view) -> auto
{
if (result.GetProcessing() == Processing::Halted)
{
AZ_Error("EntityUtilityComponent", false, "JSON %s\n", message.data());
}
else if (result.GetOutcome() > Outcomes::PartialDefaults)
{
AZ_Warning("EntityUtilityComponent", false, "JSON %s\n", message.data());
}
return result;
};
rapidjson::Document doc;
doc.Parse<rapidjson::kParseCommentsFlag>(json.data(), json.size());
ResultCode resultCode = AZ::JsonSerialization::Load(*component, doc, settings);
return resultCode.GetProcessing() != Processing::Halted;
}
AZStd::string EntityUtilityComponent::GetComponentDefaultJson(const AZStd::string& typeName)
{
AZ::TypeId typeId = GetComponentTypeIdFromName(typeName);
if (typeId.IsNull())
{
// GetComponentTypeIdFromName already does error handling
return "";
}
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(typeId);
if (!classData)
{
AZ_Error("EntityUtilityComponent", false, "Failed to find ClassData for typeId %s (%s)", typeId.ToString<AZStd::string>().c_str(), typeName.c_str());
return "";
}
void* component = classData->m_factory->Create("Component");
rapidjson::Document document;
AZ::JsonSerializerSettings settings;
settings.m_keepDefaults = true;
auto resultCode = AZ::JsonSerialization::Store(document, document.GetAllocator(), component, nullptr, typeId, settings);
// Clean up the allocated component ASAP, we don't need it anymore
classData->m_factory->Destroy(component);
if (resultCode.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
{
AZ_Error("EntityUtilityComponent", false, "Failed to serialize component to json (%s): %s",
typeName.c_str(), resultCode.ToString(typeName).c_str())
return "";
}
AZStd::string jsonString;
AZ::Outcome<void, AZStd::string> outcome = AZ::JsonSerializationUtils::WriteJsonString(document, jsonString);
if (!outcome.IsSuccess())
{
AZ_Error("EntityUtilityComponent", false, "Failed to write component json to string: %s", outcome.GetError().c_str());
return "";
}
return jsonString;
}
AZStd::vector<ComponentDetails> EntityUtilityComponent::FindMatchingComponents(const AZStd::string& searchTerm)
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
if (m_typeInfo.empty())
{
serializeContext->EnumerateDerived<AZ::Component>(
[this, serializeContext](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid& /*typeId*/)
{
auto& typeInfo = m_typeInfo.emplace_back(classData->m_typeId, classData->m_name, AZStd::vector<AZStd::string>{});
serializeContext->EnumerateBase(
[&typeInfo](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&)
{
if (classData)
{
AZStd::get<2>(typeInfo).emplace_back(classData->m_name);
}
return true;
},
classData->m_typeId);
return true;
});
}
AZStd::vector<ComponentDetails> matches;
for (const auto& [typeId, typeName, baseClasses] : m_typeInfo)
{
if (AZStd::wildcard_match(searchTerm, typeName))
{
ComponentDetails details;
details.m_typeInfo = AZStd::string::format("%s %s", typeId.ToString<AZStd::string>().c_str(), typeName.c_str());
details.m_baseClasses = baseClasses;
matches.emplace_back(AZStd::move(details));
}
}
return matches;
}
void EntityUtilityComponent::ResetEntityContext()
{
for (AZ::EntityId entityId : m_createdEntities)
{
m_entityContext->DestroyEntityById(entityId);
}
m_createdEntities.clear();
m_entityContext->ResetContext();
}
void EntityUtilityComponent::Reflect(AZ::ReflectContext* context)
{
ComponentDetails::Reflect(context);
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EntityUtilityComponent, AZ::Component>();
}
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->ConstantProperty("InvalidComponentId", BehaviorConstant(AZ::InvalidComponentId))
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Entity")
->Attribute(AZ::Script::Attributes::Module, "entity");
behaviorContext->EBus<EntityUtilityBus>("EntityUtilityBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Entity")
->Attribute(AZ::Script::Attributes::Module, "entity")
->Event("CreateEditorReadyEntity", &EntityUtilityBus::Events::CreateEditorReadyEntity)
->Event("GetOrAddComponentByTypeName", &EntityUtilityBus::Events::GetOrAddComponentByTypeName)
->Event("UpdateComponentForEntity", &EntityUtilityBus::Events::UpdateComponentForEntity)
->Event("FindMatchingComponents", &EntityUtilityBus::Events::FindMatchingComponents)
->Event("GetComponentDefaultJson", &EntityUtilityBus::Events::GetComponentDefaultJson)
;
}
}
void EntityUtilityComponent::Activate()
{
m_entityContext = AZStd::make_unique<AzFramework::EntityContext>(UtilityEntityContextId);
m_entityContext->InitContext();
EntityUtilityBus::Handler::BusConnect();
}
void EntityUtilityComponent::Deactivate()
{
EntityUtilityBus::Handler::BusDisconnect();
m_entityContext = nullptr;
}
}
@@ -0,0 +1,90 @@
/*
* 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/Component/Entity.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/ToolsComponents/EditorDisabledCompositionBus.h>
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzFramework/Entity/BehaviorEntity.h>
namespace AzToolsFramework
{
struct ComponentDetails
{
AZ_TYPE_INFO(AzToolsFramework::ComponentDetails, "{107D8379-4AD4-4547-BEE1-184B120F23E9}");
static void Reflect(AZ::ReflectContext* context);
AZStd::string m_typeInfo;
AZStd::vector<AZStd::string> m_baseClasses;
};
// This ebus is intended to provide behavior-context friendly APIs to create and manage entities
struct EntityUtilityTraits : AZ::EBusTraits
{
AZ_RTTI(AzToolsFramework::EntityUtilityTraits, "{A6305CAE-C825-43F9-A44D-E503910912AF}");
virtual ~EntityUtilityTraits() = default;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
// Creates an entity with the default editor components attached and initializes the entity
virtual AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) = 0;
virtual AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) = 0;
virtual bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) = 0;
// Gets a JSON string containing describing the default serialization state of the specified component
virtual AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) = 0;
// Returns a list of matching component type names. Supports wildcard search terms
virtual AZStd::vector<ComponentDetails> FindMatchingComponents(const AZStd::string& searchTerm) = 0;
virtual void ResetEntityContext() = 0;
};
using EntityUtilityBus = AZ::EBus<EntityUtilityTraits>;
struct EntityUtilityComponent : AZ::Component
, EntityUtilityBus::Handler
{
inline const static AZ::Uuid UtilityEntityContextId = AZ::Uuid("{9C277B88-E79E-4F8A-BAFF-A4C175BD565F}");
AZ_COMPONENT(EntityUtilityComponent, "{47205907-A0EA-4FFF-A620-04D20C04A379}");
AZ::EntityId CreateEditorReadyEntity(const AZStd::string& entityName) override;
AzFramework::BehaviorComponentId GetOrAddComponentByTypeName(AZ::EntityId entity, const AZStd::string& typeName) override;
bool UpdateComponentForEntity(AZ::EntityId entity, AzFramework::BehaviorComponentId component, const AZStd::string& json) override;
AZStd::string GetComponentDefaultJson(const AZStd::string& typeName) override;
AZStd::vector<ComponentDetails> FindMatchingComponents(const AZStd::string& searchTerm) override;
void ResetEntityContext() override;
static void Reflect(AZ::ReflectContext* context);
protected:
void Activate() override;
void Deactivate() override;
// Our own entity context. This API is intended mostly for use in Asset Builders where there is no editor context
// Additionally, an entity context is needed when using the Behavior Entity class
AZStd::unique_ptr<AzFramework::EntityContext> m_entityContext;
// TypeId, TypeName, Vector<BaseClassName>
AZStd::vector<AZStd::tuple<AZ::TypeId, AZStd::string, AZStd::vector<AZStd::string>>> m_typeInfo;
// Keep track of the entities we create so they can be reset
AZStd::vector<AZ::EntityId> m_createdEntities;
};
}; // namespace AzToolsFramework
@@ -10,6 +10,7 @@
#include <AzCore/Component/EntityId.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzFramework/Entity/EntityContextBus.h>
@@ -11,7 +11,7 @@
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/FocusMode/FocusModeNotificationBus.h>
#include <AzToolsFramework/FocusMode/FocusModeSystemComponent.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
namespace AzToolsFramework
{
@@ -79,11 +79,11 @@ namespace AzToolsFramework
{
if (!m_focusRoot.IsValid() && entityId.IsValid())
{
tracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Focus);
tracker->ActivateMode({ GetEntityContextId() }, ViewportEditorMode::Focus);
}
else if (m_focusRoot.IsValid() && !entityId.IsValid())
{
tracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Focus);
tracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Focus);
}
}
}
@@ -261,10 +261,10 @@ namespace AzToolsFramework
// ensures cursor positions are refreshed correctly with context menu focus changes)
if (eventType == QEvent::FocusIn)
{
const auto widgetCursorPosition = m_sourceWidget->mapFromGlobal(QCursor::pos());
if (m_sourceWidget->geometry().contains(widgetCursorPosition))
const auto globalCursorPosition = QCursor::pos();
if (m_sourceWidget->geometry().contains(globalCursorPosition))
{
HandleMouseMoveEvent(widgetCursorPosition);
HandleMouseMoveEvent(globalCursorPosition);
}
}
}
@@ -290,7 +290,7 @@ namespace AzToolsFramework
else if (eventType == QEvent::Type::MouseMove)
{
auto mouseEvent = static_cast<QMouseEvent*>(event);
HandleMouseMoveEvent(mouseEvent->pos());
HandleMouseMoveEvent(mouseEvent->globalPos());
}
// Map wheel events to the mouse Z movement channel.
else if (eventType == QEvent::Type::Wheel)
@@ -370,11 +370,12 @@ namespace AzToolsFramework
return QPoint{ denormalizedX, denormalizedY };
}
void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& cursorPosition)
void QtEventToAzInputMapper::HandleMouseMoveEvent(const QPoint& globalCursorPosition)
{
const QPoint cursorDelta = cursorPosition - m_previousCursorPosition;
const QPoint cursorDelta = globalCursorPosition - m_previousGlobalCursorPosition;
m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition = WidgetPositionToNormalizedPosition(cursorPosition);
m_mouseDevice->m_cursorPositionData2D->m_normalizedPosition =
WidgetPositionToNormalizedPosition(m_sourceWidget->mapFromGlobal(globalCursorPosition));
m_mouseDevice->m_cursorPositionData2D->m_normalizedPositionDelta = WidgetPositionToNormalizedPosition(cursorDelta);
ProcessPendingMouseEvents(cursorDelta);
@@ -382,12 +383,11 @@ namespace AzToolsFramework
if (m_capturingCursor)
{
// Reset our cursor position to the previous point
const QPoint screenCursorPosition = m_sourceWidget->mapToGlobal(m_previousCursorPosition);
AzQtComponents::SetCursorPos(screenCursorPosition);
AzQtComponents::SetCursorPos(m_previousGlobalCursorPosition);
}
else
{
m_previousCursorPosition = cursorPosition;
m_previousGlobalCursorPosition = globalCursorPosition;
}
}
@@ -129,7 +129,7 @@ namespace AzToolsFramework
// Handle mouse click events.
void HandleMouseButtonEvent(QMouseEvent* mouseEvent);
// Handle mouse move events.
void HandleMouseMoveEvent(const QPoint& cursorPosition);
void HandleMouseMoveEvent(const QPoint& globalCursorPosition);
// Handles key press / release events (or ShortcutOverride events for keys listed in m_highPriorityKeys).
void HandleKeyEvent(QKeyEvent* keyEvent);
// Handles mouse wheel events.
@@ -156,8 +156,8 @@ namespace AzToolsFramework
AZStd::unordered_set<Qt::Key> m_highPriorityKeys;
// A lookup table for AZ input channel ID -> physical input channel on our mouse or keyboard device.
AZStd::unordered_map<AzFramework::InputChannelId, AzFramework::InputChannel*> m_channels;
// Where the position of the mouse cursor was at the last cursor event.
QPoint m_previousCursorPosition;
// Where the mouse cursor was at the last cursor event.
QPoint m_previousGlobalCursorPosition;
// The source widget to map events from, used to calculate the relative mouse position within the widget bounds.
QWidget* m_sourceWidget;
// Flags whether or not Qt events should currently be processed.
@@ -66,7 +66,7 @@ namespace AzToolsFramework
StringFunc::Path::Join(resolveBuffer, "log", logDirectory);
fileIO->SetAlias("@log@", logDirectory.c_str());
fileIO->CreatePath("@root@");
fileIO->CreatePath("@products@");
fileIO->CreatePath("@user@");
fileIO->CreatePath("@log@");
@@ -224,6 +224,7 @@ namespace AzToolsFramework
return false;
}
AZ::Data::SerializedAssetTracker* assetTracker = settings.m_metadata.Find<AZ::Data::SerializedAssetTracker>();
referencedAssets = AZStd::move(assetTracker->GetTrackedAssets());
@@ -245,6 +246,30 @@ namespace AzToolsFramework
entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random);
}
// some assets may come in from the JSON serialzier with no AssetID, but have an asset hint
// this attempts to fix up the assets using the assetHint field
auto fixUpInvalidAssets = [](AZ::Data::Asset<AZ::Data::AssetData>& asset)
{
if (!asset.GetId().IsValid() && !asset.GetHint().empty())
{
AZ::Data::AssetId assetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetId,
&AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath,
asset.GetHint().c_str(),
AZ::Data::s_invalidAssetType,
false);
if (assetId.IsValid())
{
asset.Create(assetId, true);
}
}
};
auto tracker = AZ::Data::SerializedAssetTracker{};
tracker.SetAssetFixUp(fixUpInvalidAssets);
AZ::JsonDeserializerSettings settings;
// The InstanceEntityIdMapper is registered twice because it's used in several places during deserialization where one is
// specific for the InstanceEntityIdMapper and once for the generic JsonEntityIdMapper. Because the Json Serializer's meta
@@ -252,16 +277,17 @@ namespace AzToolsFramework
settings.m_metadata.Add(static_cast<AZ::JsonEntityIdSerializer::JsonEntityIdMapper*>(&entityIdMapper));
settings.m_metadata.Add(&entityIdMapper);
settings.m_metadata.Create<InstanceEntityScrubber>(newlyAddedEntities);
settings.m_metadata.Add(tracker);
AZStd::string scratchBuffer;
auto issueReportingCallback = [&scratchBuffer](
AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result,
AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode
AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result,
AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode
{
return Internal::JsonIssueReporter(scratchBuffer, message, result, path);
};
settings.m_reporting = AZStd::move(issueReportingCallback);
AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Load(instance, prefabDom, settings);
AZ::Data::AssetManager::Instance().ResumeAssetRelease();
@@ -8,11 +8,14 @@
#include <AzToolsFramework/Prefab/PrefabFocusHandler.h>
#include <AzToolsFramework/Commands/SelectionCommand.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.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>
#include <AzToolsFramework/Prefab/PrefabFocusUndo.h>
namespace AzToolsFramework::Prefab
{
@@ -27,15 +30,79 @@ namespace AzToolsFramework::Prefab
EditorEntityContextNotificationBus::Handler::BusConnect();
AZ::Interface<PrefabFocusInterface>::Register(this);
AZ::Interface<PrefabFocusPublicInterface>::Register(this);
}
PrefabFocusHandler::~PrefabFocusHandler()
{
AZ::Interface<PrefabFocusPublicInterface>::Unregister(this);
AZ::Interface<PrefabFocusInterface>::Unregister(this);
EditorEntityContextNotificationBus::Handler::BusDisconnect();
}
void PrefabFocusHandler::Initialize()
{
m_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
AZ_Assert(
m_containerEntityInterface,
"Prefab - PrefabFocusHandler - "
"Container Entity Interface could not be found. "
"Check that it is being correctly initialized.");
m_focusModeInterface = AZ::Interface<FocusModeInterface>::Get();
AZ_Assert(
m_focusModeInterface,
"Prefab - PrefabFocusHandler - "
"Focus Mode Interface could not be found. "
"Check that it is being correctly initialized.");
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.");
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
{
// Initialize Undo Batch object
ScopedUndoBatch undoBatch("Edit Prefab");
// Clear selection
{
const EntityIdList selectedEntities = EntityIdList{};
auto selectionUndo = aznew SelectionCommand(selectedEntities, "Clear Selection");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
}
// Edit Prefab
{
auto editUndo = aznew PrefabFocusUndo("Edit Prefab");
editUndo->Capture(entityId);
editUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, editUndo);
}
return AZ::Success();
}
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: Invalid index on FocusOnPathIndex."));
}
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
FocusOnOwningPrefab(focusedInstance->get().GetContainerEntityId());
return AZ::Success();
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId)
{
InstanceOptionalReference focusedInstance;
@@ -44,7 +111,7 @@ namespace AzToolsFramework::Prefab
PrefabEditorEntityOwnershipInterface* prefabEditorEntityOwnershipInterface =
AZ::Interface<PrefabEditorEntityOwnershipInterface>::Get();
if(!prefabEditorEntityOwnershipInterface)
if (!prefabEditorEntityOwnershipInterface)
{
return AZ::Failure(AZStd::string("Could not focus on root prefab instance - internal error "
"(PrefabEditorEntityOwnershipInterface unavailable)."));
@@ -60,18 +127,6 @@ namespace AzToolsFramework::Prefab
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: Invalid index on FocusOnPathIndex."));
}
InstanceOptionalReference focusedInstance = m_instanceFocusVector[index];
return FocusOnPrefabInstance(focusedInstance);
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnPrefabInstance(InstanceOptionalReference focusedInstance)
{
if (!focusedInstance.has_value())
@@ -79,8 +134,16 @@ namespace AzToolsFramework::Prefab
return AZ::Failure(AZStd::string("Prefab Focus Handler: invalid instance to focus on."));
}
if (!m_isInitialized)
{
Initialize();
}
if (!m_focusedInstance.has_value() || &m_focusedInstance->get() != &focusedInstance->get())
{
// Close all container entities in the old path
CloseInstanceContainers(m_instanceFocusVector);
m_focusedInstance = focusedInstance;
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
@@ -89,26 +152,21 @@ namespace AzToolsFramework::Prefab
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);
}
m_focusModeInterface->SetFocusRoot(containerEntityId);
// Refresh path variables
RefreshInstanceFocusList();
// Open all container entities in the new path
OpenInstanceContainers(m_instanceFocusVector);
PrefabFocusNotificationBus::Broadcast(&PrefabFocusNotifications::OnPrefabFocusChanged);
}
@@ -126,6 +184,17 @@ namespace AzToolsFramework::Prefab
return m_focusedInstance;
}
AZ::EntityId PrefabFocusHandler::GetFocusedPrefabContainerEntityId([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
{
if (!m_focusedInstance.has_value())
{
// PrefabFocusHandler has not been initialized yet.
return AZ::EntityId();
}
return m_focusedInstance->get().GetContainerEntityId();
}
bool PrefabFocusHandler::IsOwningPrefabBeingFocused(AZ::EntityId entityId) const
{
if (!m_focusedInstance.has_value())
@@ -156,8 +225,16 @@ namespace AzToolsFramework::Prefab
void PrefabFocusHandler::OnEntityStreamLoadSuccess()
{
if (!m_isInitialized)
{
Initialize();
}
// Clear the old focus vector
m_instanceFocusVector.clear();
// Focus on the root prefab (AZ::EntityId() will default to it)
FocusOnOwningPrefab(AZ::EntityId());
FocusOnPrefabInstanceOwningEntityId(AZ::EntityId());
}
void PrefabFocusHandler::RefreshInstanceFocusList()
@@ -184,4 +261,26 @@ namespace AzToolsFramework::Prefab
}
}
void PrefabFocusHandler::OpenInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const
{
for (const InstanceOptionalReference& instance : instances)
{
if (instance.has_value())
{
m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), true);
}
}
}
void PrefabFocusHandler::CloseInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const
{
for (const InstanceOptionalReference& instance : instances)
{
if (instance.has_value())
{
m_containerEntityInterface->SetContainerOpen(instance->get().GetContainerEntityId(), false);
}
}
}
} // namespace AzToolsFramework::Prefab
@@ -13,8 +13,15 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
namespace AzToolsFramework
{
class ContainerEntityInterface;
class FocusModeInterface;
}
namespace AzToolsFramework::Prefab
{
class InstanceEntityMapperInterface;
@@ -22,6 +29,7 @@ namespace AzToolsFramework::Prefab
//! Handles Prefab Focus mode, determining which prefab file entity changes will target.
class PrefabFocusHandler final
: private PrefabFocusInterface
, private PrefabFocusPublicInterface
, private EditorEntityContextNotificationBus::Handler
{
public:
@@ -30,11 +38,17 @@ namespace AzToolsFramework::Prefab
PrefabFocusHandler();
~PrefabFocusHandler();
void Initialize();
// PrefabFocusInterface overrides ...
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) override;
TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const override;
InstanceOptionalReference GetFocusedPrefabInstance(AzFramework::EntityContextId entityContextId) const override;
// PrefabFocusPublicInterface overrides ...
PrefabFocusOperationResult FocusOnOwningPrefab(AZ::EntityId entityId) override;
PrefabFocusOperationResult FocusOnPathIndex(AzFramework::EntityContextId entityContextId, int index) override;
AZ::EntityId GetFocusedPrefabContainerEntityId(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;
@@ -46,12 +60,19 @@ namespace AzToolsFramework::Prefab
PrefabFocusOperationResult FocusOnPrefabInstance(InstanceOptionalReference focusedInstance);
void RefreshInstanceFocusList();
void OpenInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const;
void CloseInstanceContainers(const AZStd::vector<InstanceOptionalReference>& instances) const;
InstanceOptionalReference m_focusedInstance;
TemplateId m_focusedTemplateId;
AZStd::vector<InstanceOptionalReference> m_instanceFocusVector;
AZ::IO::Path m_instanceFocusPath;
InstanceEntityMapperInterface* m_instanceEntityMapperInterface;
ContainerEntityInterface* m_containerEntityInterface = nullptr;
FocusModeInterface* m_focusModeInterface = nullptr;
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
bool m_isInitialized = false;
};
} // namespace AzToolsFramework::Prefab
@@ -20,7 +20,7 @@ namespace AzToolsFramework::Prefab
{
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
//! Interface to handle operations related to the Prefab Focus system.
//! Interface to handle internal operations related to the Prefab Focus system.
class PrefabFocusInterface
{
public:
@@ -28,29 +28,13 @@ namespace AzToolsFramework::Prefab
//! 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;
//! 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;
virtual PrefabFocusOperationResult FocusOnPrefabInstanceOwningEntityId(AZ::EntityId entityId) = 0;
//! Returns the template id of the instance the prefab system is focusing on.
virtual TemplateId GetFocusedPrefabTemplateId(AzFramework::EntityContextId entityContextId) const = 0;
//! Returns a reference to the instance the prefab system is focusing on.
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) 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,53 @@
/*
* 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 <AzFramework/Entity/EntityContext.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
namespace AzToolsFramework::Prefab
{
using PrefabFocusOperationResult = AZ::Outcome<void, AZStd::string>;
//! Public Interface for external systems to utilize the Prefab Focus system.
class PrefabFocusPublicInterface
{
public:
AZ_RTTI(PrefabFocusPublicInterface, "{53EE1D18-A41F-4DB1-9B73-9448F425722E}");
//! Set the focused prefab instance to the owning instance of the entityId provided. Supports undo/redo.
//! @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. Supports undo/redo.
//! @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 entity id of the container entity for the instance the prefab system is focusing on.
virtual AZ::EntityId GetFocusedPrefabContainerEntityId(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) 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,52 @@
/*
* 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/PrefabFocusUndo.h>
#include <AzCore/Interface/Interface.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
namespace AzToolsFramework::Prefab
{
PrefabFocusUndo::PrefabFocusUndo(const AZStd::string& undoOperationName)
: UndoSystem::URSequencePoint(undoOperationName)
{
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
AZ_Assert(m_prefabFocusInterface, "PrefabFocusUndo - Failed to grab prefab focus interface");
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
AZ_Assert(m_prefabFocusPublicInterface, "PrefabFocusUndo - Failed to grab prefab focus public interface");
}
bool PrefabFocusUndo::Changed() const
{
return true;
}
void PrefabFocusUndo::Capture(AZ::EntityId entityId)
{
auto entityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
m_beforeEntityId = m_prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(entityContextId);
m_afterEntityId = entityId;
}
void PrefabFocusUndo::Undo()
{
m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_beforeEntityId);
}
void PrefabFocusUndo::Redo()
{
m_prefabFocusInterface->FocusOnPrefabInstanceOwningEntityId(m_afterEntityId);
}
} // namespace AzToolsFramework::Prefab
@@ -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/Component/EntityId.h>
#include <AzToolsFramework/Undo/UndoSystem.h>
namespace AzToolsFramework::Prefab
{
class PrefabFocusInterface;
class PrefabFocusPublicInterface;
//! Undo node for prefab focus change operations.
class PrefabFocusUndo
: public UndoSystem::URSequencePoint
{
public:
explicit PrefabFocusUndo(const AZStd::string& undoOperationName);
bool Changed() const override;
void Capture(AZ::EntityId entityId);
void Undo() override;
void Redo() override;
protected:
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
AZ::EntityId m_beforeEntityId;
AZ::EntityId m_afterEntityId;
};
} // namespace AzToolsFramework::Prefab
@@ -50,17 +50,19 @@ namespace AzToolsFramework
auto settingsRegistry = AZ::SettingsRegistry::Get();
AZ_Assert(settingsRegistry, "Settings registry is not set");
[[maybe_unused]] bool result =
settingsRegistry->Get(m_projectPathWithOsSeparator.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath);
AZ_Warning("Prefab", result, "Couldn't retrieve project root path");
m_projectPathWithSlashSeparator = AZ::IO::Path(m_projectPathWithOsSeparator.Native(), '/').MakePreferred();
AZ::Interface<PrefabLoaderInterface>::Register(this);
m_scriptingPrefabLoader.Connect(this);
}
void PrefabLoader::UnregisterPrefabLoaderInterface()
{
m_scriptingPrefabLoader.Disconnect();
AZ::Interface<PrefabLoaderInterface>::Unregister(this);
}
@@ -568,7 +570,7 @@ namespace AzToolsFramework
(pathStr.find_first_of(AZ_FILESYSTEM_INVALID_CHARACTERS) == AZStd::string::npos) &&
(pathStr.back() != '\\' && pathStr.back() != '/');
}
AZ::IO::Path PrefabLoader::GetFullPath(AZ::IO::PathView path)
{
AZ::IO::Path pathWithOSSeparator = AZ::IO::Path(path).MakePreferred();
@@ -596,26 +598,38 @@ namespace AzToolsFramework
{
// The asset system provided us with a valid root folder and relative path, so return it.
fullPath = AZ::IO::Path(rootFolder) / assetInfo.m_relativePath;
return fullPath;
}
else
{
// If for some reason the Asset system couldn't provide a relative path, provide some fallback logic.
// Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow
// the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside
// a unit test, so just execute the fallback logic without an error.
[[maybe_unused]] bool assetProcessorReady = false;
AzFramework::AssetSystemRequestBus::BroadcastResult(
assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady);
AZ_Error(
"Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.",
AZ_STRING_ARG(path.Native()));
// If a relative path was passed in, make it relative to the project root.
fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator);
// attempt to find the absolute from the Cache folder
AZStd::string assetRootFolder;
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
settingsRegistry->Get(assetRootFolder, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
}
fullPath = AZ::IO::Path(assetRootFolder) / path;
if (fullPath.IsAbsolute() && AZ::IO::SystemFile::Exists(fullPath.c_str()))
{
return fullPath;
}
}
// If for some reason the Asset system couldn't provide a relative path, provide some fallback logic.
// Check to see if the AssetProcessor is ready. If it *is* and we didn't get a path, print an error then follow
// the fallback logic. If it's *not* ready, we're probably either extremely early in a tool startup flow or inside
// a unit test, so just execute the fallback logic without an error.
[[maybe_unused]] bool assetProcessorReady = false;
AzFramework::AssetSystemRequestBus::BroadcastResult(
assetProcessorReady, &AzFramework::AssetSystemRequestBus::Events::AssetProcessorIsReady);
AZ_Error(
"Prefab", !assetProcessorReady, "Full source path for '%.*s' could not be determined. Using fallback logic.",
AZ_STRING_ARG(path.Native()));
// If a relative path was passed in, make it relative to the project root.
fullPath = AZ::IO::Path(m_projectPathWithOsSeparator).Append(pathWithOSSeparator);
return fullPath;
}
@@ -15,6 +15,7 @@
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/string/string.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <Prefab/ScriptingPrefabLoader.h>
namespace AZ
{
@@ -114,6 +115,7 @@ namespace AzToolsFramework
void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) override;
private:
/**
* Copies the template dom provided and manipulates it into the proper format to be saved to disk.
* @param templateRef The template whose dom we want to transform into the proper format to be saved to disk.
@@ -177,6 +179,7 @@ namespace AzToolsFramework
AZStd::optional<AZStd::pair<PrefabDom, AZ::IO::Path>> StoreTemplateIntoFileFormat(TemplateId templateId);
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
ScriptingPrefabLoader m_scriptingPrefabLoader;
AZ::IO::Path m_projectPathWithOsSeparator;
AZ::IO::Path m_projectPathWithSlashSeparator;
};
@@ -99,7 +99,6 @@ namespace AzToolsFramework
// Generates a new path
static AZ::IO::Path GeneratePath();
};
} // namespace Prefab
} // namespace AzToolsFramework
@@ -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 <AzCore/Interface/Interface.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
#include <AzCore/EBus/EBus.h>
namespace AzToolsFramework
{
namespace Prefab
{
// Ebus for script-friendly APIs for the prefab loader
struct PrefabLoaderScriptingTraits : AZ::EBusTraits
{
AZ_TYPE_INFO(PrefabLoaderScriptingTraits, "{C344B7D8-8299-48C9-8450-26E1332EA011}");
virtual ~PrefabLoaderScriptingTraits() = default;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
/**
* Saves a Prefab Template into the provided output string.
* Converts Prefab Template form into .prefab form by collapsing nested Template info
* into a source path and patches.
* @param templateId Id of the template to be saved
* @return Will contain the serialized template json on success
*/
virtual AZ::Outcome<AZStd::string, void> SaveTemplateToString(TemplateId templateId) = 0;
};
using PrefabLoaderScriptingBus = AZ::EBus<PrefabLoaderScriptingTraits>;
} // namespace Prefab
} // namespace AzToolsFramework
@@ -257,9 +257,10 @@ namespace AzToolsFramework
// Select Container Entity
{
auto selectionUndo = aznew SelectionCommand({ containerEntityId }, "Select Prefab Container Entity");
const EntityIdList selectedEntities = EntityIdList{ containerEntityId };
auto selectionUndo = aznew SelectionCommand(selectedEntities, "Select Prefab Container Entity");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, selectedEntities);
}
}
@@ -1097,7 +1098,7 @@ namespace AzToolsFramework
// Select the duplicated entities/instances
auto selectionUndo = aznew SelectionCommand(duplicatedEntityAndInstanceIds, "Select Duplicated Entities/Instances");
selectionUndo->SetParent(undoBatch.GetUndoBatch());
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::RunRedoSeparately, selectionUndo);
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequestBus::Events::SetSelectedEntities, duplicatedEntityAndInstanceIds);
}
return AZ::Success();
@@ -10,6 +10,7 @@
#include <AzCore/Component/Entity.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityIdMapper.h>
@@ -36,12 +37,14 @@ namespace AzToolsFramework
m_instanceToTemplatePropagator.RegisterInstanceToTemplateInterface();
m_prefabPublicHandler.RegisterPrefabPublicHandlerInterface();
m_prefabPublicRequestHandler.Connect();
m_prefabSystemScriptingHandler.Connect(this);
AZ::SystemTickBus::Handler::BusConnect();
}
void PrefabSystemComponent::Deactivate()
{
AZ::SystemTickBus::Handler::BusDisconnect();
m_prefabSystemScriptingHandler.Disconnect();
m_prefabPublicRequestHandler.Disconnect();
m_prefabPublicHandler.UnregisterPrefabPublicHandlerInterface();
m_instanceToTemplatePropagator.UnregisterInstanceToTemplateInterface();
@@ -58,13 +61,24 @@ namespace AzToolsFramework
AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover::Reflect(context);
PrefabPublicRequestHandler::Reflect(context);
PrefabLoader::Reflect(context);
PrefabSystemScriptingHandler::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<PrefabSystemComponent, AZ::Component>()->Version(1);
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<PrefabLoaderScriptingBus>("PrefabLoaderScriptingBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "prefab")
->Attribute(AZ::Script::Attributes::Category, "Prefab")
->Event("SaveTemplateToString", &PrefabLoaderScriptingBus::Events::SaveTemplateToString);
;
}
AZ::JsonRegistrationContext* jsonRegistration = azrtti_cast<AZ::JsonRegistrationContext*>(context);
if (jsonRegistration)
{
@@ -145,7 +159,7 @@ namespace AzToolsFramework
newInstance->SetTemplateId(newTemplateId);
}
}
void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, bool immediate, InstanceOptionalReference instanceToExclude)
{
UpdatePrefabInstances(templateId, immediate, instanceToExclude);
@@ -927,7 +941,7 @@ namespace AzToolsFramework
PrefabDomValue& instance = instanceIterator->value;
AZ_Assert(instance.IsObject(), "Nested instance DOM provided is not a valid JSON object.");
PrefabDomValueReference sourceTemplateName = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::SourceName);
[[maybe_unused]] PrefabDomValueReference sourceTemplateName = PrefabDomUtils::FindPrefabDomValue(instance, PrefabDomUtils::SourceName);
AZ_Assert(sourceTemplateName, "Couldn't find source template name in the DOM of the nested instance while creating a link.");
AZ_Assert(sourceTemplateName->get() == sourceTemplate.GetFilePath().c_str(),
"The name of the source template in the nested instance DOM does not match the name of the source template already loaded");
@@ -27,6 +27,7 @@
#include <AzToolsFramework/Prefab/PrefabPublicRequestHandler.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
#include <Prefab/PrefabSystemScriptingHandler.h>
namespace AZ
{
@@ -219,7 +220,7 @@ namespace AzToolsFramework
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr,
InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) override;
PrefabDom& FindTemplateDom(TemplateId templateId) override;
/**
@@ -244,7 +245,7 @@ namespace AzToolsFramework
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.
@@ -412,6 +413,8 @@ namespace AzToolsFramework
// Handler of the public Prefab requests.
PrefabPublicRequestHandler m_prefabPublicRequestHandler;
PrefabSystemScriptingHandler m_prefabSystemScriptingHandler;
};
} // namespace Prefab
} // namespace AzToolsFramework
@@ -78,8 +78,7 @@ namespace AzToolsFramework
AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr, InstanceOptionalReference parent = AZStd::nullopt,
bool shouldCreateLinks = true) = 0;
};
} // namespace Prefab
} // namespace AzToolsFramework
@@ -0,0 +1,38 @@
/*
* 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 <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Link/Link.h>
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/EBus/EBus.h>
namespace AzToolsFramework
{
namespace Prefab
{
// Bus that exposes a script-friendly interface to the PrefabSystemComponent
struct PrefabSystemScriptingEbusTraits : AZ::EBusTraits
{
using MutexType = AZ::NullMutex;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual TemplateId CreatePrefabTemplate(
const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath) = 0;
};
using PrefabSystemScriptingBus = AZ::EBus<PrefabSystemScriptingEbusTraits>;
} // namespace Prefab
} // namespace AzToolsFramework
@@ -0,0 +1,75 @@
/*
* 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/ComponentApplicationBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <Prefab/PrefabSystemComponentInterface.h>
#include <Prefab/PrefabSystemScriptingHandler.h>
#include <AzCore/Component/Entity.h>
namespace AzToolsFramework::Prefab
{
void PrefabSystemScriptingHandler::Reflect(AZ::ReflectContext* context)
{
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->ConstantProperty("InvalidTemplateId", BehaviorConstant(InvalidTemplateId))
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "prefab")
->Attribute(AZ::Script::Attributes::Category, "Prefab");
behaviorContext->EBus<PrefabSystemScriptingBus>("PrefabSystemScriptingBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Module, "prefab")
->Attribute(AZ::Script::Attributes::Category, "Prefab")
->Event("CreatePrefab", &PrefabSystemScriptingBus::Events::CreatePrefabTemplate);
}
}
void PrefabSystemScriptingHandler::Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface)
{
AZ_Assert(prefabSystemComponentInterface != nullptr, "prefabSystemComponentInterface must not be null");
m_prefabSystemComponentInterface = prefabSystemComponentInterface;
PrefabSystemScriptingBus::Handler::BusConnect();
}
void PrefabSystemScriptingHandler::Disconnect()
{
PrefabSystemScriptingBus::Handler::BusDisconnect();
}
TemplateId PrefabSystemScriptingHandler::CreatePrefabTemplate(const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath)
{
AZStd::vector<AZ::Entity*> entities;
for (const auto& entityId : entityIds)
{
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
AZ_Warning(
"PrefabSystemComponent", entity, "EntityId %s was not found and will not be added to the prefab",
entityId.ToString().c_str());
if (entity)
{
entities.push_back(entity);
}
}
auto prefab = m_prefabSystemComponentInterface->CreatePrefab(entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)));
if (!prefab)
{
AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str());
return InvalidTemplateId;
}
return prefab->GetTemplateId();
}
}
@@ -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 <Prefab/PrefabSystemScriptingBus.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabSystemScriptingHandler
: PrefabSystemScriptingBus::Handler
{
public:
static void Reflect(AZ::ReflectContext* context);
PrefabSystemScriptingHandler() = default;
void Connect(PrefabSystemComponentInterface* prefabSystemComponentInterface);
void Disconnect();
private:
AZ_DISABLE_COPY(PrefabSystemScriptingHandler);
//////////////////////////////////////////////////////////////////////////
// PrefabSystemScriptingBus implementation
TemplateId CreatePrefabTemplate(const AZStd::vector<AZ::EntityId>& entityIds, const AZStd::string& filePath) override;
//////////////////////////////////////////////////////////////////////////
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
};
} // namespace Prefab
} // namespace AzToolsFramework
@@ -0,0 +1,144 @@
/*
* 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 <Prefab/Procedural/ProceduralPrefabAsset.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzFramework/FileFunc/FileFunc.h>
namespace AZ::Prefab
{
static constexpr const char s_useProceduralPrefabsKey[] = "/O3DE/Preferences/Prefabs/UseProceduralPrefabs";
// ProceduralPrefabAsset
ProceduralPrefabAsset::ProceduralPrefabAsset(const AZ::Data::AssetId& assetId)
: AZ::Data::AssetData(assetId)
, m_templateId(AzToolsFramework::Prefab::InvalidTemplateId)
{
}
void ProceduralPrefabAsset::Reflect(AZ::ReflectContext* context)
{
PrefabDomData::Reflect(context);
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
{
serializeContext->Class<ProceduralPrefabAsset, AZ::Data::AssetData>()
->Version(1)
->Field("Template Name", &ProceduralPrefabAsset::m_templateName)
->Field("Template ID", &ProceduralPrefabAsset::m_templateId);
}
}
const AZStd::string& ProceduralPrefabAsset::GetTemplateName() const
{
return m_templateName;
}
void ProceduralPrefabAsset::SetTemplateName(AZStd::string templateName)
{
m_templateName = AZStd::move(templateName);
}
AzToolsFramework::Prefab::TemplateId ProceduralPrefabAsset::GetTemplateId() const
{
return m_templateId;
}
void ProceduralPrefabAsset::SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId)
{
m_templateId = templateId;
}
bool ProceduralPrefabAsset::UseProceduralPrefabs()
{
bool useProceduralPrefabs = false;
bool result = AZ::SettingsRegistry::Get()->GetObject(useProceduralPrefabs, s_useProceduralPrefabsKey);
return result && useProceduralPrefabs;
}
// PrefabDomData
void PrefabDomData::Reflect(AZ::ReflectContext* context)
{
if (auto* jsonContext = azrtti_cast<AZ::JsonRegistrationContext*>(context))
{
jsonContext->Serializer<PrefabDomDataJsonSerializer>()->HandlesType<PrefabDomData>();
}
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<PrefabDomData>()
->Version(1);
}
}
void PrefabDomData::CopyValue(const rapidjson::Value& inputValue)
{
m_prefabDom.CopyFrom(inputValue, m_prefabDom.GetAllocator());
}
const AzToolsFramework::Prefab::PrefabDom& PrefabDomData::GetValue() const
{
return m_prefabDom;
}
// PrefabDomDataJsonSerializer
AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Load(
void* outputValue,
[[maybe_unused]] const AZ::Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
AZ::JsonDeserializerContext& context)
{
AZ_Assert(outputValueTypeId == azrtti_typeid<PrefabDomData>(),
"PrefabDomDataJsonSerializer Load against output typeID that was not PrefabDomData");
AZ_Assert(outputValue, "PrefabDomDataJsonSerializer Load against null output");
namespace JSR = AZ::JsonSerializationResult;
JSR::ResultCode result(JSR::Tasks::ReadField);
if (inputValue.IsObject() == false)
{
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing object"));
return context.Report(result, "Prefab should be an object.");
}
if (inputValue.MemberCount() < 1)
{
result.Combine(context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Missing, "Missing members"));
return context.Report(result, "Prefab should have multiple members.");
}
auto* outputVariable = reinterpret_cast<PrefabDomData*>(outputValue);
outputVariable->CopyValue(inputValue);
return context.Report(result, "Loaded procedural prefab");
}
AZ::JsonSerializationResult::Result PrefabDomDataJsonSerializer::Store(
rapidjson::Value& outputValue,
const void* inputValue,
[[maybe_unused]] const void* defaultValue,
[[maybe_unused]] const AZ::Uuid& valueTypeId,
AZ::JsonSerializerContext& context)
{
AZ_Assert(inputValue, "Input value for PrefabDomDataJsonSerializer can't be null.");
AZ_Assert(azrtti_typeid<PrefabDomData>() == valueTypeId,
"Unable to Serialize because the provided type is not PrefabGroup::PrefabDomData.");
const PrefabDomData* prefabDomData = reinterpret_cast<const PrefabDomData*>(inputValue);
namespace JSR = AZ::JsonSerializationResult;
JSR::ResultCode result(JSR::Tasks::WriteValue);
outputValue.SetObject();
outputValue.CopyFrom(prefabDomData->GetValue(), context.GetJsonAllocator());
return context.Report(result, "Stored procedural prefab");
}
}
@@ -0,0 +1,90 @@
/*
* 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/Asset/AssetCommon.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
namespace AZ::Prefab
{
//! A wrapper around the JSON DOM type so that the assets can read in and write out
//! JSON directly since Prefabs are JSON serialized entity-component data
class PrefabDomData final
{
public:
AZ_RTTI(PrefabDomData, "{C73A3360-D772-4D41-9118-A039BF9340C1}");
AZ_CLASS_ALLOCATOR(PrefabDomData, AZ::SystemAllocator, 0);
PrefabDomData() = default;
~PrefabDomData() = default;
static void Reflect(AZ::ReflectContext* context);
void CopyValue(const rapidjson::Value& inputValue);
const AzToolsFramework::Prefab::PrefabDom& GetValue() const;
private:
AzToolsFramework::Prefab::PrefabDom m_prefabDom;
};
//! Registered to help read/write JSON for the PrefabDomData::m_prefabDom
class PrefabDomDataJsonSerializer final
: public AZ::BaseJsonSerializer
{
public:
AZ_RTTI(PrefabDomDataJsonSerializer, "{9FC48652-A00B-4EFA-8FD9-345A8E625439}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR(PrefabDomDataJsonSerializer, AZ::SystemAllocator, 0);
~PrefabDomDataJsonSerializer() override = default;
AZ::JsonSerializationResult::Result Load(
void* outputValue,
const AZ::Uuid& outputValueTypeId,
const rapidjson::Value& inputValue,
AZ::JsonDeserializerContext& context) override;
AZ::JsonSerializationResult::Result Store(
rapidjson::Value& outputValue,
const void* inputValue,
const void* defaultValue,
const AZ::Uuid& valueTypeId,
AZ::JsonSerializerContext& context) override;
};
//! An asset type to register templates into the Prefab system so that they
//! can instantiate like Authored Prefabs
class ProceduralPrefabAsset
: public AZ::Data::AssetData
{
public:
AZ_CLASS_ALLOCATOR(ProceduralPrefabAsset, AZ::SystemAllocator, 0);
AZ_RTTI(ProceduralPrefabAsset, "{9B7C8459-471E-4EAD-A363-7990CC4065A9}", AZ::Data::AssetData);
static bool UseProceduralPrefabs();
ProceduralPrefabAsset(const AZ::Data::AssetId& assetId = AZ::Data::AssetId());
~ProceduralPrefabAsset() override = default;
ProceduralPrefabAsset(const ProceduralPrefabAsset& rhs) = delete;
ProceduralPrefabAsset& operator=(const ProceduralPrefabAsset& rhs) = delete;
const AZStd::string& GetTemplateName() const;
void SetTemplateName(AZStd::string templateName);
AzToolsFramework::Prefab::TemplateId GetTemplateId() const;
void SetTemplateId(AzToolsFramework::Prefab::TemplateId templateId);
static void Reflect(AZ::ReflectContext* context);
private:
AZStd::string m_templateName;
AzToolsFramework::Prefab::TemplateId m_templateId;
};
}
@@ -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
*
*/
#include <Prefab/ScriptingPrefabLoader.h>
namespace AzToolsFramework::Prefab
{
void ScriptingPrefabLoader::Connect(PrefabLoaderInterface* prefabLoaderInterface)
{
AZ_Assert(prefabLoaderInterface, "prefabLoaderInterface must not be null");
m_prefabLoaderInterface = prefabLoaderInterface;
PrefabLoaderScriptingBus::Handler::BusConnect();
}
void ScriptingPrefabLoader::Disconnect()
{
PrefabLoaderScriptingBus::Handler::BusDisconnect();
}
AZ::Outcome<AZStd::string, void> ScriptingPrefabLoader::SaveTemplateToString(TemplateId templateId)
{
AZStd::string json;
if (m_prefabLoaderInterface->SaveTemplateToString(templateId, json))
{
return AZ::Success(json);
}
return AZ::Failure();
}
} // namespace AzToolsFramework::Prefab
@@ -0,0 +1,42 @@
/*
* 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 <Prefab/PrefabLoaderInterface.h>
#include <Prefab/PrefabLoaderScriptingBus.h>
namespace AzToolsFramework
{
namespace Prefab
{
/**
* The Scripting Prefab Loader handles scripting-friendly API requests for the prefab loader
*/
class ScriptingPrefabLoader
: private PrefabLoaderScriptingBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ScriptingPrefabLoader, AZ::SystemAllocator, 0);
AZ_RTTI(ScriptingPrefabLoader, "{ABC3C989-4D4F-41E7-B25B-B0FEF97177E6}");
void Connect(PrefabLoaderInterface* prefabLoaderInterface);
void Disconnect();
private:
//////////////////////////////////////////////////////////////////////////
// PrefabLoaderRequestBus implementation
AZ::Outcome<AZStd::string, void> SaveTemplateToString(TemplateId templateId) override;
//////////////////////////////////////////////////////////////////////////
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
};
} // namespace Prefab
} // namespace AzToolsFramework
@@ -8,6 +8,7 @@
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/IO/FileOperations.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
@@ -78,13 +79,9 @@ namespace AzToolsFramework
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(entitiesAndDescendants,
&AzToolsFramework::ToolsApplicationRequestBus::Events::GatherEntitiesAndAllDescendents, AzToolsFramework::EntityIdList{ entityId });
// Retrieve the game folder so we can use that as a root with the passed in relative path
const char* gameFolder = nullptr;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(gameFolder, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetAbsoluteDevGameFolderPath);
// Join our relative path with the game folder to get a full path to the desired asset
AZStd::string assetFullPath;
AzFramework::StringFunc::Path::Join(gameFolder, assetPath, assetFullPath);
AZ::IO::FixedMaxPath assetFullPath = AZ::Utils::GetProjectPath();
assetFullPath /= assetPath;
// Call SliceUtilities::MakeNewSlice with all user input prompts disabled
bool success = AzToolsFramework::SliceUtilities::MakeNewSlice(entitiesAndDescendants,
@@ -718,7 +718,7 @@ namespace AzToolsFramework
if (!fullPathFound)
{
assetFullPath = AZStd::string::format("@devassets@/%s", sliceAssetPath.c_str());
assetFullPath = AZStd::string::format("@projectroot@/%s", sliceAssetPath.c_str());
}
return Commit(assetFullPath.c_str(), preSaveCallback, postSaveCallback, sliceCommitFlags);
@@ -1020,13 +1020,13 @@ namespace AzToolsFramework
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO, "File IO is not initialized.");
AZStd::string devAssetPath = fileIO->GetAlias("@devassets@");
AZStd::string devAssetPath = fileIO->GetAlias("@projectroot@");
AZStd::string userPath = fileIO->GetAlias("@user@");
AZStd::string tempPath = fullPath;
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, devAssetPath);
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, userPath);
EBUS_EVENT(AzFramework::ApplicationRequests::Bus, NormalizePath, tempPath);
AzFramework::StringFunc::Replace(tempPath, "@devassets@", devAssetPath.c_str());
AzFramework::StringFunc::Replace(tempPath, "@projectroot@", devAssetPath.c_str());
AzFramework::StringFunc::Replace(tempPath, devAssetPath.c_str(), userPath.c_str());
tempPath.append(".slicetemp");
@@ -90,7 +90,7 @@ namespace AzToolsFramework
typedef SharedThumbnailKey BusIdType;
//! notify product thumbnail that the data is ready
virtual void ThumbnailRendered(QPixmap& thumbnailImage) = 0;
virtual void ThumbnailRendered(const QPixmap& thumbnailImage) = 0;
//! notify product thumbnail that the thumbnail failed to render
virtual void ThumbnailFailedToRender() = 0;
};
@@ -378,11 +378,11 @@ namespace AzToolsFramework
// If this layer is being loaded, it won't have a level save dependency yet, so clear that flag.
m_mustSaveLevelWhenLayerSaves = false;
QString fullPathName = levelPakFile;
if (fullPathName.contains("@devassets@"))
if (fullPathName.contains("@projectroot@"))
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
// Resolving the path through resolvepath would normalize and lowcase it, and in this case, we don't want that.
fullPathName.replace("@devassets@", fileIO->GetAlias("@devassets@"));
fullPathName.replace("@projectroot@", fileIO->GetAlias("@projectroot@"));
}
QFileInfo fileNameInfo(fullPathName);
@@ -1025,7 +1025,7 @@ namespace AzToolsFramework
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/LuaScript.svg")
->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid())
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Script.png")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/lua-script/")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/scripting/lua-script/")
->DataElement("AssetRef", &ScriptEditorComponent::m_scriptAsset, "Script", "Which script to use")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &ScriptEditorComponent::ScriptHasChanged)
->Attribute("BrowseIcon", ":/stylesheet/img/UI20/browse-edit-select-files.svg")
@@ -37,51 +37,71 @@ namespace AzToolsFramework
return m_handlerId;
}
QString EditorEntityUiHandlerBase::GenerateItemInfoString(AZ::EntityId /*entityId*/) const
QString EditorEntityUiHandlerBase::GenerateItemInfoString([[maybe_unused]] AZ::EntityId entityId) const
{
return QString();
}
QString EditorEntityUiHandlerBase::GenerateItemTooltip(AZ::EntityId /*entityId*/) const
QString EditorEntityUiHandlerBase::GenerateItemTooltip([[maybe_unused]] AZ::EntityId entityId) const
{
return QString();
}
QIcon EditorEntityUiHandlerBase::GenerateItemIcon(AZ::EntityId /*entityId*/) const
QIcon EditorEntityUiHandlerBase::GenerateItemIcon([[maybe_unused]] AZ::EntityId entityId) const
{
return QIcon();
}
bool EditorEntityUiHandlerBase::CanToggleLockVisibility(AZ::EntityId /*entityId*/) const
bool EditorEntityUiHandlerBase::CanToggleLockVisibility([[maybe_unused]] AZ::EntityId entityId) const
{
return true;
}
bool EditorEntityUiHandlerBase::CanRename(AZ::EntityId /*entityId*/) const
bool EditorEntityUiHandlerBase::CanRename([[maybe_unused]] AZ::EntityId entityId) const
{
return true;
}
void EditorEntityUiHandlerBase::PaintItemBackground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const
void EditorEntityUiHandlerBase::PaintItemBackground(
[[maybe_unused]] QPainter* painter,
[[maybe_unused]] const QStyleOptionViewItem& option,
[[maybe_unused]] const QModelIndex& index) const
{
}
void EditorEntityUiHandlerBase::PaintDescendantBackground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/,
const QModelIndex& /*descendantIndex*/) const
void EditorEntityUiHandlerBase::PaintDescendantBackground(
[[maybe_unused]] QPainter* painter,
[[maybe_unused]] const QStyleOptionViewItem& option,
[[maybe_unused]] const QModelIndex& index,
[[maybe_unused]] const QModelIndex& descendantIndex) const
{
}
void EditorEntityUiHandlerBase::PaintDescendantBranchBackground(QPainter* /*painter*/, const QTreeView* /*view*/, const QRect& /*rect*/,
const QModelIndex& /*index*/, const QModelIndex& /*descendantIndex*/) const
void EditorEntityUiHandlerBase::PaintDescendantBranchBackground(
[[maybe_unused]] QPainter* painter,
[[maybe_unused]] const QTreeView* view,
[[maybe_unused]] const QRect& rect,
[[maybe_unused]] const QModelIndex& index,
[[maybe_unused]] const QModelIndex& descendantIndex) const
{
}
void EditorEntityUiHandlerBase::PaintItemForeground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const
void EditorEntityUiHandlerBase::PaintItemForeground(
[[maybe_unused]] QPainter* painter,
[[maybe_unused]] const QStyleOptionViewItem& option,
[[maybe_unused]] const QModelIndex& index) const
{
}
void EditorEntityUiHandlerBase::PaintDescendantForeground(QPainter* /*painter*/, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/,
const QModelIndex& /*descendantIndex*/) const
void EditorEntityUiHandlerBase::PaintDescendantForeground(
[[maybe_unused]] QPainter* painter,
[[maybe_unused]] const QStyleOptionViewItem& option,
[[maybe_unused]] const QModelIndex& index,
[[maybe_unused]] const QModelIndex& descendantIndex) const
{
}
void EditorEntityUiHandlerBase::OnDoubleClick([[maybe_unused]] AZ::EntityId entityId) const
{
}
@@ -61,6 +61,9 @@ namespace AzToolsFramework
virtual void PaintDescendantForeground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const;
//! Triggered when the entity is double clicked in the Outliner.
virtual void OnDoubleClick(AZ::EntityId entityId) const;
private:
EditorEntityUiHandlerId m_handlerId = 0;
};
@@ -88,6 +88,7 @@ namespace AzToolsFramework
EntityOutlinerListModel::~EntityOutlinerListModel()
{
ContainerEntityNotificationBus::Handler::BusDisconnect();
EditorEntityInfoNotificationBus::Handler::BusDisconnect();
EditorEntityContextNotificationBus::Handler::BusDisconnect();
ToolsApplicationEvents::Bus::Handler::BusDisconnect();
@@ -105,6 +106,12 @@ namespace AzToolsFramework
EntityCompositionNotificationBus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(
editorEntityContextId, &AzToolsFramework::EditorEntityContextRequestBus::Events::GetEditorEntityContextId);
ContainerEntityNotificationBus::Handler::BusConnect(editorEntityContextId);
m_editorEntityUiInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert(m_editorEntityUiInterface != nullptr,
"EntityOutlinerListModel requires a EditorEntityUiInterface instance on Initialize.");
@@ -1333,12 +1340,26 @@ namespace AzToolsFramework
emit EnableSelectionUpdates(true);
}
void EntityOutlinerListModel::OnEntityRuntimeActivationChanged(AZ::EntityId entityId, bool activeOnStart)
void EntityOutlinerListModel::OnEntityRuntimeActivationChanged(AZ::EntityId entityId, [[maybe_unused]] bool activeOnStart)
{
AZ_UNUSED(activeOnStart);
QueueEntityUpdate(entityId);
}
void EntityOutlinerListModel::OnContainerEntityStatusChanged(AZ::EntityId entityId, [[maybe_unused]] bool open)
{
QModelIndex changedIndex = GetIndexFromEntity(entityId);
// Trigger a refresh of all direct children so that they can be shown or hidden appropriately.
int numChildren = rowCount(changedIndex);
if (numChildren > 0)
{
emit dataChanged(index(0, 0, changedIndex), index(numChildren - 1, ColumnCount - 1, changedIndex));
}
// Always expand containers
QueueEntityToExpand(entityId, true);
}
void EntityOutlinerListModel::OnEntityInfoUpdatedRemoveChildBegin([[maybe_unused]] AZ::EntityId parentId, [[maybe_unused]] AZ::EntityId childId)
{
//add/remove operations trigger selection change signals which assert and break undo/redo operations in progress in inspector etc.
@@ -17,6 +17,7 @@
#include <AzToolsFramework/API/EntityCompositionNotificationBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityNotificationBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/EditorEntityRuntimeActivationBus.h>
@@ -54,6 +55,7 @@ namespace AzToolsFramework
, private EntityCompositionNotificationBus::Handler
, private EditorEntityRuntimeActivationChangeNotificationBus::Handler
, private AZ::EntitySystemBus::Handler
, private ContainerEntityNotificationBus::Handler
{
Q_OBJECT;
@@ -216,6 +218,9 @@ namespace AzToolsFramework
// EditorEntityRuntimeActivationChangeNotificationBus::Handler
void OnEntityRuntimeActivationChanged(AZ::EntityId entityId, bool activeOnStart) override;
// ContainerEntityNotificationBus overrides ...
void OnContainerEntityStatusChanged(AZ::EntityId entityId, bool open) override;
// Drag/Drop of components from Component Palette.
bool dropMimeDataComponentPalette(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
@@ -11,6 +11,8 @@
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Component/Entity.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include "EntityOutlinerListModel.hxx"
namespace AzToolsFramework
@@ -19,6 +21,10 @@ namespace AzToolsFramework
EntityOutlinerSortFilterProxyModel::EntityOutlinerSortFilterProxyModel(QObject* pParent)
: QSortFilterProxyModel(pParent)
{
m_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
AZ_Assert(
m_containerEntityInterface != nullptr,
"EntityOutlinerContainerProxyModel requires a ContainerEntityInterface instance on construction.");
}
void EntityOutlinerSortFilterProxyModel::UpdateFilter()
@@ -26,8 +32,24 @@ namespace AzToolsFramework
invalidateFilter();
}
void EntityOutlinerSortFilterProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
{
QSortFilterProxyModel::setSourceModel(sourceModel);
m_listModel = qobject_cast<EntityOutlinerListModel*>(sourceModel);
AZ_Assert(m_listModel != nullptr, "EntityOutlinerContainerProxyModel requires an EntityOutlinerListModel as its source .");
}
bool EntityOutlinerSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
// Retrieve the entityId of the parent entity
AZ::EntityId parentEntityId = m_listModel->GetEntityFromIndex(sourceParent);
if(!m_containerEntityInterface->IsContainerOpen(parentEntityId))
{
return false;
}
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
QVariant visibilityData = sourceModel()->data(index, EntityOutlinerListModel::VisibilityRole);
return visibilityData.isValid() ? visibilityData.toBool() : true;
@@ -19,11 +19,12 @@
namespace AzToolsFramework
{
class ContainerEntityInterface;
class EntityOutlinerListModel;
/*!
* Enables the Outliner to filter entries based on search string.
* Enables the Outliner to do custom sorting on entries.
*/
//! Enables the Outliner to filter entries based on search string.
//! Enables the Outliner to do custom sorting on entries.
//! Enforces the correct rendering for container entities.
class EntityOutlinerSortFilterProxyModel
: public QSortFilterProxyModel
{
@@ -37,12 +38,15 @@ namespace AzToolsFramework
void UpdateFilter();
// Qt overrides
void setSourceModel(QAbstractItemModel* sourceModel) override;
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
bool lessThan(const QModelIndex& left, const QModelIndex& right) const override;
void sort(int column, Qt::SortOrder order) override;
private:
QString m_filterName;
EntityOutlinerListModel* m_listModel = nullptr;
ContainerEntityInterface* m_containerEntityInterface = nullptr;
};
}
@@ -26,6 +26,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiHandlerBase.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerDisplayOptionsMenu.h>
@@ -198,6 +199,7 @@ namespace AzToolsFramework
m_proxyModel = aznew EntityOutlinerSortFilterProxyModel(this);
m_proxyModel->setSourceModel(m_listModel);
m_gui->m_objectTree->setModel(m_proxyModel);
// Link up signals for informing the model of tree changes using the proxy as an intermediary
@@ -291,8 +293,7 @@ namespace AzToolsFramework
EntityOutlinerModelNotificationBus::Handler::BusConnect();
ToolsApplicationEvents::Bus::Handler::BusConnect();
EditorEntityContextNotificationBus::Handler::BusConnect();
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(
GetEntityContextId());
ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId());
EditorEntityInfoNotificationBus::Handler::BusConnect();
Prefab::PrefabPublicNotificationBus::Handler::BusConnect();
EditorWindowUIRequestBus::Handler::BusConnect();
@@ -302,7 +303,7 @@ namespace AzToolsFramework
{
EditorWindowUIRequestBus::Handler::BusDisconnect();
Prefab::PrefabPublicNotificationBus::Handler::BusDisconnect();
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
EditorEntityInfoNotificationBus::Handler::BusDisconnect();
EditorPickModeNotificationBus::Handler::BusDisconnect();
EntityHighlightMessages::Bus::Handler::BusDisconnect();
@@ -323,7 +324,8 @@ namespace AzToolsFramework
// Currently, the first behavior is implemented.
void EntityOutlinerWidget::OnSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
if (m_selectionChangeInProgress || !m_enableSelectionUpdates)
if (m_selectionChangeInProgress || !m_enableSelectionUpdates
|| (selected.empty() && deselected.empty()))
{
return;
}
@@ -905,8 +907,12 @@ namespace AzToolsFramework
}
}
void EntityOutlinerWidget::OnTreeItemDoubleClicked(const QModelIndex& /*index*/)
void EntityOutlinerWidget::OnTreeItemDoubleClicked(const QModelIndex& index)
{
if (AZ::EntityId entityId = GetEntityIdFromIndex(index); auto entityUiHandler = m_editorEntityUiInterface->GetHandler(entityId))
{
entityUiHandler->OnDoubleClick(entityId);
}
}
void EntityOutlinerWidget::OnTreeItemExpanded(const QModelIndex& index)
@@ -1123,14 +1129,22 @@ namespace AzToolsFramework
EnableUi(enable);
}
void EntityOutlinerWidget::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
void EntityOutlinerWidget::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
EnableUi(false);
if (mode == ViewportEditorMode::Component)
{
EnableUi(false);
}
}
void EntityOutlinerWidget::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
void EntityOutlinerWidget::OnEditorModeDeactivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
EnableUi(true);
if (mode == ViewportEditorMode::Component)
{
EnableUi(true);
}
}
void EntityOutlinerWidget::OnPrefabInstancePropagationBegin()
@@ -1142,7 +1156,7 @@ namespace AzToolsFramework
{
QTimer::singleShot(1, this, [this]() {
m_gui->m_objectTree->setUpdatesEnabled(true);
m_gui->m_objectTree->expand(m_proxyModel->index(0,0));
m_gui->m_objectTree->expandToDepth(0);
});
}
@@ -14,7 +14,7 @@
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
@@ -41,6 +41,7 @@ namespace AzToolsFramework
{
class EditorEntityUiInterface;
class EntityOutlinerListModel;
class EntityOutlinerContainerProxyModel;
class EntityOutlinerSortFilterProxyModel;
namespace EntityOutliner
@@ -58,7 +59,7 @@ namespace AzToolsFramework
, private ToolsApplicationEvents::Bus::Handler
, private EditorEntityContextNotificationBus::Handler
, private EditorEntityInfoNotificationBus::Handler
, private ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private ViewportEditorModeNotificationsBus::Handler
, private Prefab::PrefabPublicNotificationBus::Handler
, private EditorWindowUIRequestBus::Handler
{
@@ -100,9 +101,11 @@ namespace AzToolsFramework
void OnEntityInfoUpdatedAddChildEnd(AZ::EntityId /*parentId*/, AZ::EntityId /*childId*/) override;
void OnEntityInfoUpdatedName(AZ::EntityId entityId, const AZStd::string& /*name*/) override;
// EditorComponentModeNotificationBus
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
// ViewportEditorModeNotificationsBus overrides ...
void OnEditorModeActivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
void OnEditorModeDeactivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
// PrefabPublicNotificationBus
void OnPrefabInstancePropagationBegin() override;
@@ -117,6 +120,7 @@ namespace AzToolsFramework
Ui::EntityOutlinerWidgetUI* m_gui;
EntityOutlinerListModel* m_listModel;
EntityOutlinerContainerProxyModel* m_containerModel;
EntityOutlinerSortFilterProxyModel* m_proxyModel;
AZ::u64 m_selectionContextId;
AZStd::vector<AZ::EntityId> m_selectedEntityIds;
@@ -13,6 +13,7 @@
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Asset/AssetSystemBus.h>
@@ -22,8 +23,12 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
#include <AzToolsFramework/ToolsComponents/EditorLayerComponentBus.h>
#include <AzToolsFramework/UI/EditorEntityUi/EditorEntityUiInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
@@ -34,7 +39,6 @@
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/Widgets/CardHeader.h>
#include <QApplication>
#include <QCheckBox>
#include <QDialog>
@@ -51,14 +55,13 @@
#include <QVBoxLayout>
#include <QWidget>
namespace AzToolsFramework
{
namespace Prefab
{
ContainerEntityInterface* PrefabIntegrationManager::s_containerEntityInterface = nullptr;
EditorEntityUiInterface* PrefabIntegrationManager::s_editorEntityUiInterface = nullptr;
PrefabFocusInterface* PrefabIntegrationManager::s_prefabFocusInterface = nullptr;
PrefabFocusPublicInterface* PrefabIntegrationManager::s_prefabFocusPublicInterface = nullptr;
PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr;
PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr;
PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr;
@@ -89,6 +92,13 @@ namespace AzToolsFramework
PrefabIntegrationManager::PrefabIntegrationManager()
{
s_containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get();
if (s_containerEntityInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get ContainerEntityInterface on PrefabIntegrationManager construction.");
return;
}
s_editorEntityUiInterface = AZ::Interface<EditorEntityUiInterface>::Get();
if (s_editorEntityUiInterface == nullptr)
{
@@ -117,14 +127,15 @@ namespace AzToolsFramework
return;
}
s_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
if (s_prefabFocusInterface == nullptr)
s_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
if (s_prefabFocusPublicInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabIntegrationManager construction.");
AZ_Assert(false, "Prefab - could not get PrefabFocusPublicInterface on PrefabIntegrationManager construction.");
return;
}
EditorContextMenuBus::Handler::BusConnect();
EditorEventsBus::Handler::BusConnect();
PrefabInstanceContainerNotificationBus::Handler::BusConnect();
AZ::Interface<PrefabIntegrationInterface>::Register(this);
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusConnect(s_prefabFileExtension);
@@ -135,6 +146,7 @@ namespace AzToolsFramework
AssetBrowser::AssetBrowserSourceDropBus::Handler::BusDisconnect();
AZ::Interface<PrefabIntegrationInterface>::Unregister(this);
PrefabInstanceContainerNotificationBus::Handler::BusDisconnect();
EditorEventsBus::Handler::BusDisconnect();
EditorContextMenuBus::Handler::BusDisconnect();
}
@@ -210,6 +222,16 @@ namespace AzToolsFramework
instantiateAction, &QAction::triggered, instantiateAction, [] { ContextMenu_InstantiatePrefab(); });
}
// Instantiate Procedural Prefab
if (AZ::Prefab::ProceduralPrefabAsset::UseProceduralPrefabs())
{
QAction* action = menu->addAction(QObject::tr("Instantiate Procedural Prefab..."));
action->setToolTip(QObject::tr("Instantiates a procedural prefab file in a prefab."));
QObject::connect(
action, &QAction::triggered, action, [] { ContextMenu_InstantiateProceduralPrefab(); });
}
menu->addSeparator();
bool itemWasShown = false;
@@ -223,12 +245,8 @@ namespace AzToolsFramework
if (s_prefabPublicInterface->IsInstanceContainerEntity(selectedEntity))
{
// Edit Prefab
if (prefabWipFeaturesEnabled)
if (prefabWipFeaturesEnabled && !s_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(selectedEntity))
{
bool beingEdited = s_prefabFocusInterface->IsOwningPrefabBeingFocused(selectedEntity);
if (!beingEdited)
{
QAction* editAction = menu->addAction(QObject::tr("Edit Prefab"));
editAction->setToolTip(QObject::tr("Edit the prefab in focus mode."));
@@ -237,7 +255,6 @@ namespace AzToolsFramework
});
itemWasShown = true;
}
}
// Save Prefab
@@ -291,6 +308,11 @@ namespace AzToolsFramework
}
}
void PrefabIntegrationManager::OnEscape()
{
s_prefabFocusPublicInterface->FocusOnOwningPrefab(AZ::EntityId());
}
void PrefabIntegrationManager::HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const
{
auto instantiatePrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(sourceFilePath, parentId, position);
@@ -307,7 +329,7 @@ namespace AzToolsFramework
// temporarily null after QFileDialogs close, which we need in order to
// be able to parent our message dialogs properly
QWidget* activeWindow = QApplication::activeWindow();
const AZStd::string prefabFilesPath = "@devassets@/Prefabs";
const AZStd::string prefabFilesPath = "@projectroot@/Prefabs";
// Remove Level entity if it's part of the list
@@ -427,9 +449,41 @@ namespace AzToolsFramework
}
}
void PrefabIntegrationManager::ContextMenu_InstantiateProceduralPrefab()
{
AZStd::string prefabAssetPath;
bool hasUserForProceduralPrefabAsset = QueryUserForProceduralPrefabAsset(prefabAssetPath);
if (hasUserForProceduralPrefabAsset)
{
AZ::EntityId parentId;
AZ::Vector3 position = AZ::Vector3::CreateZero();
EntityIdList selectedEntities;
ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
if (selectedEntities.size() == 1)
{
parentId = selectedEntities.front();
AZ::TransformBus::EventResult(position, parentId, &AZ::TransformInterface::GetWorldTranslation);
}
else
{
// otherwise return since it needs to be inside an authored prefab
return;
}
// Instantiating from context menu always puts the instance at the root level
auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabAssetPath, parentId, position);
if (!createPrefabOutcome.IsSuccess())
{
WarnUserOfError("Prefab Instantiation Error", createPrefabOutcome.GetError());
}
}
}
void PrefabIntegrationManager::ContextMenu_EditPrefab(AZ::EntityId containerEntity)
{
s_prefabFocusInterface->FocusOnOwningPrefab(containerEntity);
s_prefabFocusPublicInterface->FocusOnOwningPrefab(containerEntity);
}
void PrefabIntegrationManager::ContextMenu_SavePrefab(AZ::EntityId containerEntity)
@@ -682,6 +736,33 @@ namespace AzToolsFramework
return true;
}
bool PrefabIntegrationManager::QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath)
{
using namespace AzToolsFramework;
auto selection = AssetBrowser::AssetSelectionModel::AssetTypeSelection(azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>());
EditorRequests::Bus::Broadcast(&AzToolsFramework::EditorRequests::BrowseForAssets, selection);
if (!selection.IsValid())
{
return false;
}
auto product = azrtti_cast<const ProductAssetBrowserEntry*>(selection.GetResult());
if (product == nullptr)
{
return false;
}
outPrefabAssetPath = product->GetRelativePath();
auto asset = AZ::Data::AssetManager::Instance().GetAsset(
product->GetAssetId(),
azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>(),
AZ::Data::AssetLoadBehavior::Default);
return asset.BlockUntilLoadComplete() != AZ::Data::AssetData::AssetStatus::Error;
}
void PrefabIntegrationManager::WarnUserOfError(AZStd::string_view title, AZStd::string_view message)
{
QWidget* activeWindow = QApplication::activeWindow();
@@ -1057,6 +1138,7 @@ namespace AzToolsFramework
void PrefabIntegrationManager::OnPrefabComponentActivate(AZ::EntityId entityId)
{
// Register entity to appropriate UI Handler for UI overrides
if (s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId))
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_levelRootUiHandler.GetHandlerId());
@@ -1064,11 +1146,32 @@ namespace AzToolsFramework
else
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
bool prefabWipFeaturesEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
if (prefabWipFeaturesEnabled)
{
// Register entity as a container
s_containerEntityInterface->RegisterEntityAsContainer(entityId);
}
}
}
void PrefabIntegrationManager::OnPrefabComponentDeactivate(AZ::EntityId entityId)
{
bool prefabWipFeaturesEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
if (prefabWipFeaturesEnabled && !s_prefabPublicInterface->IsLevelInstanceContainerEntity(entityId))
{
// Unregister entity as a container
s_containerEntityInterface->UnregisterEntityAsContainer(entityId);
}
// Unregister entity from UI Handler
s_editorEntityUiInterface->UnregisterEntity(entityId);
}
@@ -1290,7 +1393,7 @@ namespace AzToolsFramework
AZStd::unique_ptr<AzQtComponents::Card> PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId)
{
FlowLayout* unsavedPrefabsLayout = new FlowLayout(AzToolsFramework::GetActiveWindow());
FlowLayout* unsavedPrefabsLayout = new FlowLayout(nullptr);
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId);
@@ -26,9 +26,11 @@
namespace AzToolsFramework
{
class ContainerEntityInterface;
namespace Prefab
{
class PrefabFocusInterface;
class PrefabFocusPublicInterface;
class PrefabLoaderInterface;
//! Structure for saving/retrieving user settings related to prefab workflows.
@@ -49,6 +51,7 @@ namespace AzToolsFramework
class PrefabIntegrationManager final
: public EditorContextMenuBus::Handler
, public EditorEventsBus::Handler
, public AssetBrowser::AssetBrowserSourceDropBus::Handler
, public PrefabInstanceContainerNotificationBus::Handler
, public PrefabIntegrationInterface
@@ -62,19 +65,22 @@ namespace AzToolsFramework
static void Reflect(AZ::ReflectContext* context);
// EditorContextMenuBus...
// EditorContextMenuBus overrides ...
int GetMenuPosition() const override;
AZStd::string GetMenuIdentifier() const override;
void PopulateEditorGlobalContextMenu(QMenu* menu, const AZ::Vector2& point, int flags) override;
// EntityOutlinerSourceDropHandlingBus...
// EditorEventsBus overrides ...
void OnEscape();
// EntityOutlinerSourceDropHandlingBus overrides ...
void HandleSourceFileType(AZStd::string_view sourceFilePath, AZ::EntityId parentId, AZ::Vector3 position) const override;
// PrefabInstanceContainerNotificationBus...
// PrefabInstanceContainerNotificationBus overrides ...
void OnPrefabComponentActivate(AZ::EntityId entityId) override;
void OnPrefabComponentDeactivate(AZ::EntityId entityId) override;
// PrefabIntegrationInterface...
// PrefabIntegrationInterface overrides ...
AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override;
int ExecuteClosePrefabDialog(TemplateId templateId) override;
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
@@ -89,6 +95,7 @@ namespace AzToolsFramework
// Context menu item handlers
static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities);
static void ContextMenu_InstantiatePrefab();
static void ContextMenu_InstantiateProceduralPrefab();
static void ContextMenu_EditPrefab(AZ::EntityId containerEntity);
static void ContextMenu_SavePrefab(AZ::EntityId containerEntity);
static void ContextMenu_DeleteSelected();
@@ -99,6 +106,7 @@ namespace AzToolsFramework
const AZStd::string& suggestedName, const char* initialTargetDirectory, AZ::u32 prefabUserSettingsId, QWidget* activeWindow,
AZStd::string& outPrefabName, AZStd::string& outPrefabFilePath);
static bool QueryUserForPrefabFilePath(AZStd::string& outPrefabFilePath);
static bool QueryUserForProceduralPrefabAsset(AZStd::string& outPrefabAssetPath);
static void WarnUserOfError(AZStd::string_view title, AZStd::string_view message);
// Path and filename generation
@@ -134,8 +142,9 @@ namespace AzToolsFramework
static const AZStd::string s_prefabFileExtension;
static ContainerEntityInterface* s_containerEntityInterface;
static EditorEntityUiInterface* s_editorEntityUiInterface;
static PrefabFocusInterface* s_prefabFocusInterface;
static PrefabFocusPublicInterface* s_prefabFocusPublicInterface;
static PrefabLoaderInterface* s_prefabLoaderInterface;
static PrefabPublicInterface* s_prefabPublicInterface;
static PrefabSystemComponentInterface* s_prefabSystemComponentInterface;
@@ -8,7 +8,9 @@
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/UI/Outliner/EntityOutlinerListModel.hxx>
@@ -33,10 +35,10 @@ namespace AzToolsFramework
return;
}
m_prefabFocusInterface = AZ::Interface<Prefab::PrefabFocusInterface>::Get();
if (m_prefabFocusInterface == nullptr)
m_prefabFocusPublicInterface = AZ::Interface<Prefab::PrefabFocusPublicInterface>::Get();
if (m_prefabFocusPublicInterface == nullptr)
{
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusInterface on PrefabUiHandler construction.");
AZ_Assert(false, "PrefabUiHandler - could not get PrefabFocusPublicInterface on PrefabUiHandler construction.");
return;
}
}
@@ -81,7 +83,7 @@ namespace AzToolsFramework
QIcon PrefabUiHandler::GenerateItemIcon(AZ::EntityId entityId) const
{
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
{
return QIcon(m_prefabEditIconPath);
}
@@ -103,7 +105,7 @@ namespace AzToolsFramework
const bool hasVisibleChildren = index.data(EntityOutlinerListModel::ExpandedRole).value<bool>() && index.model()->hasChildren(index);
QColor backgroundColor = m_prefabCapsuleColor;
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
{
backgroundColor = m_prefabCapsuleEditColor;
}
@@ -189,7 +191,7 @@ namespace AzToolsFramework
const bool isLastColumn = descendantIndex.column() == EntityOutlinerListModel::ColumnLockToggle;
QColor borderColor = m_prefabCapsuleColor;
if (m_prefabFocusInterface->IsOwningPrefabBeingFocused(entityId))
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
{
borderColor = m_prefabCapsuleEditColor;
}
@@ -317,4 +319,17 @@ namespace AzToolsFramework
return Internal_GetLastVisibleChild(model, lastChild);
}
void PrefabUiHandler::OnDoubleClick(AZ::EntityId entityId) const
{
bool prefabWipFeaturesEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabWipFeaturesEnabled, &AzFramework::ApplicationRequests::ArePrefabWipFeaturesEnabled);
if (prefabWipFeaturesEnabled)
{
// Focus on this prefab
m_prefabFocusPublicInterface->FocusOnOwningPrefab(entityId);
}
}
}
@@ -15,7 +15,7 @@ namespace AzToolsFramework
namespace Prefab
{
class PrefabFocusInterface;
class PrefabFocusPublicInterface;
class PrefabPublicInterface;
};
@@ -29,16 +29,17 @@ namespace AzToolsFramework
PrefabUiHandler();
~PrefabUiHandler() override = default;
// EditorEntityUiHandler...
// EditorEntityUiHandler overrides ...
QString GenerateItemInfoString(AZ::EntityId entityId) const override;
QString GenerateItemTooltip(AZ::EntityId entityId) const override;
QIcon GenerateItemIcon(AZ::EntityId entityId) const override;
void PaintItemBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void PaintDescendantBackground(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index,
const QModelIndex& descendantIndex) const override;
void OnDoubleClick(AZ::EntityId entityId) const override;
private:
Prefab::PrefabFocusInterface* m_prefabFocusInterface = nullptr;
Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
static bool IsLastVisibleChild(const QModelIndex& parent, const QModelIndex& child);
@@ -8,7 +8,7 @@
#include <AzToolsFramework/UI/Prefab/PrefabViewportFocusPathHandler.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
namespace AzToolsFramework::Prefab
{
@@ -31,8 +31,8 @@ namespace AzToolsFramework::Prefab
void PrefabViewportFocusPathHandler::Initialize(AzQtComponents::BreadCrumbs* breadcrumbsWidget, QToolButton* backButton)
{
// Get reference to the PrefabFocusInterface handler
m_prefabFocusInterface = AZ::Interface<PrefabFocusInterface>::Get();
if (m_prefabFocusInterface == nullptr)
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
if (m_prefabFocusPublicInterface == nullptr)
{
AZ_Assert(false, "Prefab - could not get PrefabFocusInterface on PrefabViewportFocusPathHandler construction.");
return;
@@ -46,7 +46,7 @@ namespace AzToolsFramework::Prefab
connect(m_breadcrumbsWidget, &AzQtComponents::BreadCrumbs::linkClicked, this,
[&](const QString&, int linkIndex)
{
m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex);
m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, linkIndex);
}
);
@@ -54,9 +54,9 @@ namespace AzToolsFramework::Prefab
connect(m_backButton, &QToolButton::clicked, this,
[&]()
{
if (int length = m_prefabFocusInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1)
if (int length = m_prefabFocusPublicInterface->GetPrefabFocusPathLength(m_editorEntityContextId); length > 1)
{
m_prefabFocusInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2);
m_prefabFocusPublicInterface->FocusOnPathIndex(m_editorEntityContextId, length - 2);
}
}
);
@@ -65,7 +65,7 @@ namespace AzToolsFramework::Prefab
void PrefabViewportFocusPathHandler::OnPrefabFocusChanged()
{
// Push new Path
m_breadcrumbsWidget->pushPath(m_prefabFocusInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
m_breadcrumbsWidget->pushPath(m_prefabFocusPublicInterface->GetPrefabFocusPath(m_editorEntityContextId).c_str());
}
} // namespace AzToolsFramework::Prefab
@@ -19,7 +19,7 @@
namespace AzToolsFramework::Prefab
{
class PrefabFocusInterface;
class PrefabFocusPublicInterface;
class PrefabViewportFocusPathHandler
: public PrefabFocusNotificationBus::Handler
@@ -40,6 +40,6 @@ namespace AzToolsFramework::Prefab
AzFramework::EntityContextId m_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
PrefabFocusInterface* m_prefabFocusInterface = nullptr;
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
};
} // namespace AzToolsFramework::Prefab
@@ -90,7 +90,6 @@ namespace AzToolsFramework
void SetComponentOverridden(const bool overridden);
// Calls match EditorComponentModeNotificationBus - called from EntityPropertyEditor
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes);
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes);
void ActiveComponentModeChanged(const AZ::Uuid& componentType);
@@ -33,6 +33,7 @@ AZ_POP_DISABLE_WARNING
#include <AzQtComponents/Components/Style.h>
#include <AzQtComponents/Components/Widgets/DragAndDrop.h>
#include <AzQtComponents/Components/Widgets/LineEdit.h>
#include <AzToolsFramework/API/ComponentModeCollectionInterface.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
@@ -486,8 +487,12 @@ namespace AzToolsFramework
, m_isSystemEntityEditor(false)
, m_isLevelEntityEditor(isLevelEntityEditor)
{
initEntityPropertyEditorResources();
m_componentModeCollection = AZ::Interface<ComponentModeCollectionInterface>::Get();
AZ_Assert(m_componentModeCollection, "Could not retrieve component mode collection.");
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
AZ_Assert(m_prefabPublicInterface != nullptr, "EntityPropertyEditor requires a PrefabPublicInterface instance on Initialize.");
@@ -5698,39 +5703,49 @@ namespace AzToolsFramework
SaveComponentEditorState();
}
void EntityPropertyEditor::EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes)
void EntityPropertyEditor::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
DisableComponentActions(this, m_entityComponentActions);
SetPropertyEditorState(m_gui, false);
m_disabled = true;
if (!componentModeTypes.empty())
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
m_componentEditorLastSelectedIndex = GetComponentEditorIndexFromType(componentModeTypes.front());
}
DisableComponentActions(this, m_entityComponentActions);
SetPropertyEditorState(m_gui, false);
const auto componentModeTypes = m_componentModeCollection->GetComponentTypes();
m_disabled = true;
if (!componentModeTypes.empty())
{
m_componentEditorLastSelectedIndex = GetComponentEditorIndexFromType(componentModeTypes.front());
}
for (auto componentEditor : m_componentEditors)
{
componentEditor->EnteredComponentMode(componentModeTypes);
}
for (auto componentEditor : m_componentEditors)
{
componentEditor->EnteredComponentMode(componentModeTypes);
}
// record the selected state after entering component mode
SaveComponentEditorState();
// record the selected state after entering component mode
SaveComponentEditorState();
}
}
void EntityPropertyEditor::LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes)
void EntityPropertyEditor::OnEditorModeDeactivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
EnableComponentActions(this, m_entityComponentActions);
SetPropertyEditorState(m_gui, true);
m_disabled = false;
for (auto componentEditor : m_componentEditors)
if (mode == AzToolsFramework::ViewportEditorMode::Component)
{
componentEditor->LeftComponentMode(componentModeTypes);
}
EnableComponentActions(this, m_entityComponentActions);
SetPropertyEditorState(m_gui, true);
const auto componentModeTypes = m_componentModeCollection->GetComponentTypes();
m_disabled = false;
// record the selected state after leaving component mode
SaveComponentEditorState();
for (auto componentEditor : m_componentEditors)
{
componentEditor->LeftComponentMode(componentModeTypes);
}
// record the selected state after leaving component mode
SaveComponentEditorState();
}
}
void EntityPropertyEditor::ActiveComponentModeChanged(const AZ::Uuid& componentType)
@@ -26,6 +26,7 @@
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/EntityPropertyEditorRequestsBus.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
@@ -59,6 +60,7 @@ namespace AzToolsFramework
{
class ComponentEditor;
class ComponentPaletteWidget;
class ComponentModeCollectionInterface;
struct SourceControlFileInfo;
namespace AssetBrowser
@@ -108,6 +110,7 @@ namespace AzToolsFramework
, public AzToolsFramework::EditorEntityContextNotificationBus::Handler
, public AzToolsFramework::EntityPropertyEditorRequestBus::Handler
, public AzToolsFramework::PropertyEditorEntityChangeNotificationBus::MultiHandler
, private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
, public EditorInspectorComponentNotificationBus::MultiHandler
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, public AZ::EntitySystemBus::Handler
@@ -231,10 +234,14 @@ namespace AzToolsFramework
//////////////////////////////////////////////////////////////////////////
// EditorComponentModeNotificationBus
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void ActiveComponentModeChanged(const AZ::Uuid& componentType) override;
// ViewportEditorModeNotificationsBus overrides ...
void OnEditorModeActivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
void OnEditorModeDeactivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
// EntityPropertEditorRequestBus
void GetSelectedAndPinnedEntities(EntityIdList& selectedEntityIds) override;
void GetSelectedEntities(EntityIdList& selectedEntityIds) override;
@@ -627,6 +634,8 @@ namespace AzToolsFramework
float m_moveFadeSecondsRemaining;
AZStd::vector<int> m_indexMapOfMovedRow;
AzToolsFramework::ComponentModeCollectionInterface* m_componentModeCollection = nullptr;
// When m_initiatingPropertyChangeNotification is set to true, it means this EntityPropertyEditor is
// broadcasting a change to all listeners about a property change for a given entity. This is needed
// so that we don't update the values twice for this inspector
@@ -28,6 +28,9 @@ AZ_PUSH_DISABLE_WARNING(4244 4251, "-Wunknown-warning-option")
#include <QTableView>
#include <QHeaderView>
#include <QPainter>
#include <QPixmap>
#include <QByteArray>
#include <QDataStream>
AZ_POP_DISABLE_WARNING
#include <AzCore/Asset/AssetManager.h>
@@ -1230,6 +1233,16 @@ namespace AzToolsFramework
return m_showThumbnailDropDownButton;
}
void PropertyAssetCtrl::SetCustomThumbnailEnabled(bool enabled)
{
m_thumbnail->SetCustomThumbnailEnabled(enabled);
}
void PropertyAssetCtrl::SetCustomThumbnailPixmap(const QPixmap& pixmap)
{
m_thumbnail->SetCustomThumbnailPixmap(pixmap);
}
void PropertyAssetCtrl::SetThumbnailCallback(EditCallbackType* editNotifyCallback)
{
m_thumbnailCallback = editNotifyCallback;
@@ -1356,15 +1369,27 @@ namespace AzToolsFramework
GUI->SetClearNotifyCallback(nullptr);
}
}
else if (attrib == AZ_CRC("BrowseIcon", 0x507d7a4f))
else if (attrib == AZ_CRC_CE("BrowseIcon"))
{
AZStd::string iconPath;
attrValue->Read<AZStd::string>(iconPath);
if (!iconPath.empty())
if (attrValue->Read<AZStd::string>(iconPath) && !iconPath.empty())
{
GUI->SetBrowseButtonIcon(QIcon(iconPath.c_str()));
}
else
{
// A QPixmap object can't be assigned directly via an attribute.
// This allows dynamic icon data to be supplied as a buffer containing a serialized QPixmap.
AZStd::vector<char> pixmapBuffer;
if (attrValue->Read<AZStd::vector<char>>(pixmapBuffer) && !pixmapBuffer.empty())
{
QByteArray pixmapBytes(pixmapBuffer.data(), aznumeric_cast<int>(pixmapBuffer.size()));
QDataStream stream(&pixmapBytes, QIODevice::ReadOnly);
QPixmap pixmap;
stream >> pixmap;
GUI->SetBrowseButtonIcon(pixmap);
}
}
}
else if (attrib == AZ_CRC_CE("BrowseButtonEnabled"))
{
@@ -1390,6 +1415,30 @@ namespace AzToolsFramework
GUI->SetShowThumbnail(showThumbnail);
}
}
else if (attrib == AZ_CRC_CE("ThumbnailIcon"))
{
AZStd::string iconPath;
if (attrValue->Read<AZStd::string>(iconPath) && !iconPath.empty())
{
GUI->SetCustomThumbnailEnabled(true);
GUI->SetCustomThumbnailPixmap(QPixmap::fromImage(QImage(iconPath.c_str())));
}
else
{
// A QPixmap object can't be assigned directly via an attribute.
// This allows dynamic icon data to be supplied as a buffer containing a serialized QPixmap.
AZStd::vector<char> pixmapBuffer;
if (attrValue->Read<AZStd::vector<char>>(pixmapBuffer) && !pixmapBuffer.empty())
{
QByteArray pixmapBytes(pixmapBuffer.data(), aznumeric_cast<int>(pixmapBuffer.size()));
QDataStream stream(&pixmapBytes, QIODevice::ReadOnly);
QPixmap pixmap;
stream >> pixmap;
GUI->SetCustomThumbnailEnabled(true);
GUI->SetCustomThumbnailPixmap(pixmap);
}
}
}
else if (attrib == AZ_CRC_CE("ThumbnailCallback"))
{
PropertyAssetCtrl::EditCallbackType* func = azdynamic_cast<PropertyAssetCtrl::EditCallbackType*>(attrValue->GetAttribute());
@@ -217,12 +217,17 @@ namespace AzToolsFramework
void SetHideProductFilesInAssetPicker(bool hide);
bool GetHideProductFilesInAssetPicker() const;
// Enable and configure a thumbnail widget that displays an asset preview and dropdown arrow for a dropdown menu
void SetShowThumbnail(bool enable);
bool GetShowThumbnail() const;
void SetShowThumbnailDropDownButton(bool enable);
bool GetShowThumbnailDropDownButton() const;
void SetThumbnailCallback(EditCallbackType* editNotifyCallback);
// If enabled, replaces the thumbnail widget content with a custom pixmap
void SetCustomThumbnailEnabled(bool enabled);
void SetCustomThumbnailPixmap(const QPixmap& pixmap);
void SetSelectedAssetID(const AZ::Data::AssetId& newID);
void SetCurrentAssetType(const AZ::Data::AssetType& newType);
void SetSelectedAssetID(const AZ::Data::AssetId& newID, const AZ::Data::AssetType& newType);
@@ -38,7 +38,7 @@ namespace AzToolsFramework
static bool UnsignedToolTip(QWidget* widget, QString& toolTipString);
};
//! Base class for integer widget handlers to provide functionality independant
//! Base class for integer widget handlers to provide functionality independent
//! of widget type.
//! @tparam ValueType The integer primitive type of the handler.
//! @tparam PropertyControl The widget type of the handler.
@@ -167,8 +167,7 @@ namespace AzToolsFramework
PropertyControl* newCtrl = aznew PropertyControl(pParent);
this->connect(newCtrl, &PropertyControl::valueChanged, this, [newCtrl]()
{
EBUS_EVENT(PropertyEditorGUIMessages::Bus, RequestWrite, newCtrl);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Events::RequestWrite, newCtrl);
});
// note: Qt automatically disconnects objects from each other when either end is destroyed, no need to worry about delete.
@@ -98,11 +98,6 @@ namespace AzToolsFramework
QWidget* IntSpinBoxHandler<ValueType>::CreateGUI(QWidget* parent)
{
PropertyIntSpinCtrl* newCtrl = static_cast<PropertyIntSpinCtrl*>(BaseHandler::CreateGUI(parent));
this->connect(newCtrl, &PropertyIntSpinCtrl::valueChanged, [newCtrl]()
{
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&PropertyEditorGUIMessages::Bus::Handler::RequestWrite, newCtrl);
});
return newCtrl;
}
@@ -1102,10 +1102,7 @@ namespace AzToolsFramework
{
m_dropDownArrow->hide();
}
m_indent->changeSize((m_treeDepth * m_treeIndentation) + m_leafIndentation, 1, QSizePolicy::Fixed, QSizePolicy::Fixed);
m_leftHandSideLayout->invalidate();
m_leftHandSideLayout->update();
m_leftHandSideLayout->activate();
SetIndentSize(m_treeDepth * m_treeIndentation + m_leafIndentation);
}
else
{
@@ -1117,10 +1114,7 @@ namespace AzToolsFramework
connect(m_dropDownArrow, &QCheckBox::clicked, this, &PropertyRowWidget::OnClickedExpansionButton);
}
m_dropDownArrow->show();
m_indent->changeSize((m_treeDepth * m_treeIndentation), 1, QSizePolicy::Fixed, QSizePolicy::Fixed);
m_leftHandSideLayout->invalidate();
m_leftHandSideLayout->update();
m_leftHandSideLayout->activate();
SetIndentSize(m_treeDepth * m_treeIndentation);
m_dropDownArrow->setChecked(m_expanded);
}
}
@@ -1720,10 +1714,9 @@ namespace AzToolsFramework
}
else
{
m_indicatorButton->setVisible(true);
QPixmap pixmap(imagePath);
m_indicatorButton->setIcon(pixmap);
m_indicatorButton->setVisible(true);
};
}
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/PropertyEditor/Resources">
<file>blank.png</file>
<file>point_hand.png</file>
<file>cross-circle-small.png</file>
<file>cross-small.png</file>
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:81b5fa1f978888c3be8a40fce20455668df2723a77587aeb7039f8bf74bdd0e3
size 119
@@ -7,75 +7,117 @@
*/
#include <AzToolsFramework/Debug/TraceContext.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer<QRawFontPrivate>' needs to have dll-interface to be used by clients of class 'QRawFont'
// 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning)
#include <QLabel>
#include <QHBoxLayout>
#include <QEvent>
#include <QPainter>
#include <UI/UICore/AspectRatioAwarePixmapWidget.hxx>
#include <Thumbnails/ThumbnailWidget.h>
// 4251: 'QRawFont::d': class 'QExplicitlySharedDataPointer<QRawFontPrivate>' needs to have dll-interface to be used by clients of class
// 'QRawFont' 4800: 'QTextEngine *const ': forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QApplication>
#include <QEvent>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <Thumbnails/ThumbnailWidget.h>
#include <UI/UICore/AspectRatioAwarePixmapWidget.hxx>
AZ_POP_DISABLE_WARNING
#include "ThumbnailPropertyCtrl.h"
namespace AzToolsFramework
{
ThumbnailPropertyCtrl::ThumbnailPropertyCtrl(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* pLayout = new QHBoxLayout();
pLayout->setContentsMargins(0, 0, 0, 0);
pLayout->setSpacing(0);
m_thumbnail = new Thumbnailer::ThumbnailWidget(this);
m_thumbnail->setFixedSize(QSize(24, 24));
m_thumbnailEnlarged = new Thumbnailer::ThumbnailWidget(this);
m_thumbnailEnlarged->setFixedSize(QSize(180, 180));
m_thumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
m_customThumbnail = new QLabel(this);
m_customThumbnail->setFixedSize(QSize(24, 24));
m_customThumbnail->setScaledContents(true);
m_customThumbnailEnlarged = new QLabel(this);
m_customThumbnailEnlarged->setFixedSize(QSize(180, 180));
m_customThumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
m_customThumbnailEnlarged->setScaledContents(true);
m_dropDownArrow = new AspectRatioAwarePixmapWidget(this);
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png"));
m_dropDownArrow->setFixedSize(QSize(8, 24));
ShowDropDownArrow(false);
m_emptyThumbnail = new QLabel(this);
m_emptyThumbnail->setPixmap(QPixmap(":/stylesheet/img/line.png"));
m_emptyThumbnail->setFixedSize(QSize(24, 24));
pLayout->addWidget(m_emptyThumbnail);
QHBoxLayout* pLayout = new QHBoxLayout();
pLayout->setContentsMargins(0, 0, 0, 0);
pLayout->setSpacing(0);
pLayout->addWidget(m_thumbnail);
pLayout->addWidget(m_customThumbnail);
pLayout->addWidget(m_emptyThumbnail);
pLayout->addSpacing(4);
pLayout->addWidget(m_dropDownArrow);
pLayout->addSpacing(4);
setLayout(pLayout);
ShowDropDownArrow(false);
UpdateVisibility();
}
void ThumbnailPropertyCtrl::SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName)
{
m_key = key;
m_emptyThumbnail->setVisible(false);
m_thumbnail->SetThumbnailKey(key, contextName);
if (m_customThumbnailEnabled)
{
ClearThumbnail();
}
else
{
m_key = key;
m_thumbnail->SetThumbnailKey(m_key, contextName);
m_thumbnailEnlarged->SetThumbnailKey(m_key, contextName);
}
UpdateVisibility();
}
void ThumbnailPropertyCtrl::ClearThumbnail()
{
m_emptyThumbnail->setVisible(true);
m_key.clear();
m_thumbnail->ClearThumbnail();
m_thumbnailEnlarged->ClearThumbnail();
UpdateVisibility();
}
void ThumbnailPropertyCtrl::ShowDropDownArrow(bool visible)
{
if (visible)
{
setFixedSize(QSize(40, 24));
}
else
{
setFixedSize(QSize(24, 24));
}
setFixedSize(QSize(visible ? 40 : 24, 24));
m_dropDownArrow->setVisible(visible);
}
void ThumbnailPropertyCtrl::SetCustomThumbnailEnabled(bool enabled)
{
m_customThumbnailEnabled = enabled;
UpdateVisibility();
}
void ThumbnailPropertyCtrl::SetCustomThumbnailPixmap(const QPixmap& pixmap)
{
m_customThumbnail->setPixmap(pixmap);
m_customThumbnailEnlarged->setPixmap(pixmap);
UpdateVisibility();
}
void ThumbnailPropertyCtrl::UpdateVisibility()
{
m_thumbnail->setVisible(m_key && !m_customThumbnailEnabled);
m_thumbnailEnlarged->setVisible(false);
m_customThumbnail->setVisible(m_customThumbnailEnabled);
m_customThumbnailEnlarged->setVisible(false);
m_emptyThumbnail->setVisible(!m_key && !m_customThumbnailEnabled);
}
bool ThumbnailPropertyCtrl::event(QEvent* e)
{
if (isEnabled())
@@ -83,7 +125,7 @@ namespace AzToolsFramework
if (e->type() == QEvent::MouseButtonPress)
{
emit clicked();
return true; //ignore
return true; // ignore
}
}
@@ -94,37 +136,32 @@ namespace AzToolsFramework
{
QPainter p(this);
QRect targetRect(QPoint(), QSize(40, 24));
p.fillRect(targetRect, QColor(17, 17, 17)); // #111111
p.fillRect(targetRect, QColor("#111111"));
QWidget::paintEvent(e);
}
void ThumbnailPropertyCtrl::enterEvent(QEvent* e)
{
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0_highlighted.png"));
if (!m_thumbnailEnlarged && m_key)
{
QPoint position = mapToGlobal(pos() - QPoint(185, 0));
QSize size(180, 180);
m_thumbnailEnlarged.reset(new Thumbnailer::ThumbnailWidget());
m_thumbnailEnlarged->setFixedSize(size);
m_thumbnailEnlarged->move(position);
m_thumbnailEnlarged->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
m_thumbnailEnlarged->SetThumbnailKey(m_key);
m_thumbnailEnlarged->raise();
m_thumbnailEnlarged->show();
}
const QPoint offset(-m_thumbnailEnlarged->width() - 5, -m_thumbnailEnlarged->height() / 2 + m_thumbnail->height() / 2);
m_thumbnailEnlarged->move(mapToGlobal(pos()) + offset);
m_thumbnailEnlarged->raise();
m_thumbnailEnlarged->setVisible(m_key && !m_customThumbnailEnabled);
m_customThumbnailEnlarged->move(mapToGlobal(pos()) + offset);
m_customThumbnailEnlarged->raise();
m_customThumbnailEnlarged->setVisible(m_customThumbnailEnabled);
QWidget::enterEvent(e);
}
void ThumbnailPropertyCtrl::leaveEvent(QEvent* e)
{
m_dropDownArrow->setPixmap(QPixmap(":/stylesheet/img/triangle0.png"));
if (m_thumbnailEnlarged)
{
m_thumbnailEnlarged.reset();
}
m_thumbnailEnlarged->setVisible(false);
m_customThumbnailEnlarged->setVisible(false);
QWidget::leaveEvent(e);
}
}
} // namespace AzToolsFramework
#include "UI/PropertyEditor/moc_ThumbnailPropertyCtrl.cpp"
@@ -1,5 +1,3 @@
#pragma once
/*
* 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.
@@ -8,6 +6,8 @@
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/PlatformDef.h>
#include <AzToolsFramework/Thumbnails/Thumbnail.h>
@@ -35,25 +35,38 @@ namespace AzToolsFramework
//! Call this to set what thumbnail widget will display
void SetThumbnailKey(Thumbnailer::SharedThumbnailKey key, const char* contextName = "Default");
//! Remove current thumbnail
void ClearThumbnail();
//! Display a clickable dropdown arrow next to the thumbnail
void ShowDropDownArrow(bool visible);
bool event(QEvent* e) override;
//! Override the thumbnail widget with a custom image
void SetCustomThumbnailEnabled(bool enabled);
//! Assign a custom image to display in place of thumbnail
void SetCustomThumbnailPixmap(const QPixmap& pixmap);
Q_SIGNALS:
void clicked();
protected:
private:
void UpdateVisibility();
bool event(QEvent* e) override;
void paintEvent(QPaintEvent* e) override;
void enterEvent(QEvent* e) override;
void leaveEvent(QEvent* e) override;
private:
Thumbnailer::SharedThumbnailKey m_key;
Thumbnailer::ThumbnailWidget* m_thumbnail = nullptr;
QScopedPointer<Thumbnailer::ThumbnailWidget> m_thumbnailEnlarged;
Thumbnailer::ThumbnailWidget* m_thumbnailEnlarged = nullptr;
QLabel* m_customThumbnail = nullptr;
QLabel* m_customThumbnailEnlarged = nullptr;
bool m_customThumbnailEnabled = false;
QLabel* m_emptyThumbnail = nullptr;
AspectRatioAwarePixmapWidget* m_dropDownArrow = nullptr;
};
@@ -129,7 +129,7 @@ namespace UnitTest
using AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus;
AzToolsFramework::EditorActionRequestBus::Handler::BusConnect();
EditorComponentModeNotificationBus::Handler::BusConnect(GetEntityContextId());
ViewportEditorModeNotificationsBus::Handler::BusConnect(GetEntityContextId());
m_defaultWidget.setFocus();
}
@@ -137,18 +137,26 @@ namespace UnitTest
{
using AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus;
EditorComponentModeNotificationBus::Handler::BusDisconnect();
ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
AzToolsFramework::EditorActionRequestBus::Handler::BusDisconnect();
}
void TestEditorActions::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentTypes)
void TestEditorActions::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
m_componentModeWidget.setFocus();
if (mode == ViewportEditorMode::Component)
{
m_componentModeWidget.setFocus();
}
}
void TestEditorActions::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentTypes)
void TestEditorActions::OnEditorModeDeactivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
m_defaultWidget.setFocus();
if (mode == ViewportEditorMode::Component)
{
m_defaultWidget.setFocus();
}
}
void TestEditorActions::AddActionViaBus(int id, QAction* action)
@@ -23,9 +23,9 @@
#include <AZTestShared/Utils/Utils.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Entity/EditorEntityTransformBus.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/Viewport/ActionBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
@@ -103,7 +103,7 @@ namespace UnitTest
/// component mode editing.
class TestEditorActions
: private AzToolsFramework::EditorActionRequestBus::Handler
, private AzToolsFramework::ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private AzToolsFramework::ViewportEditorModeNotificationsBus::Handler
{
// EditorActionRequestBus ...
void AddActionViaBus(int id, QAction* action) override;
@@ -114,9 +114,11 @@ namespace UnitTest
void AttachOverride(QWidget* /*object*/) override {}
void DetachOverride() override {}
// EditorComponentModeNotificationBus ...
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentTypes) override;
// ViewportEditorModeNotificationsBus overrides ...
void OnEditorModeActivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
void OnEditorModeDeactivated(
const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode) override;
public:
void Connect();
@@ -27,19 +27,28 @@ namespace AzToolsFramework
, m_viewportEditorModeTracker(viewportEditorModeTracker)
, m_componentModeCollection(viewportEditorModeTracker)
{
AZ_Assert(
AZ::Interface<ComponentModeCollectionInterface>::Get() == nullptr, "Unexpected registration of component mode collection.")
AZ::Interface<ComponentModeCollectionInterface>::Register(&m_componentModeCollection);
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);
m_viewportEditorModeTracker->ActivateMode({ GetEntityContextId() }, ViewportEditorMode::Default);
}
EditorDefaultSelection::~EditorDefaultSelection()
{
ComponentModeFramework::ComponentModeSystemRequestBus::Handler::BusDisconnect();
ActionOverrideRequestBus::Handler::BusDisconnect();
m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Default);
m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Default);
AZ_Assert(
AZ::Interface<ComponentModeCollectionInterface>::Get() != nullptr,
"Unexpected unregistration of component mode collection.")
AZ::Interface<ComponentModeCollectionInterface>::Unregister(&m_componentModeCollection);
}
void EditorDefaultSelection::SetOverridePhantomWidget(QWidget* phantomOverrideWidget)
@@ -13,8 +13,9 @@
#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/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/ToolsComponents/EditorEntityIconComponentBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h>
@@ -186,11 +187,18 @@ namespace AzToolsFramework
}
// Verify if the entity Id corresponds to an entity that is focused; if not, halt selection.
if (!m_focusModeInterface->IsInFocusSubTree(entityIdUnderCursor))
if (!IsSelectableAccordingToFocusMode(entityIdUnderCursor))
{
return AZ::EntityId();
}
// Container Entity support - if the entity that is being selected is part of a closed container,
// change the selection to the container instead.
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
{
return containerEntityInterface->FindHighestSelectableEntity(entityIdUnderCursor);
}
return entityIdUnderCursor;
}
@@ -208,7 +216,7 @@ namespace AzToolsFramework
{
const AZ::EntityId entityId = m_entityDataCache->GetVisibleEntityId(entityCacheIndex);
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex))
if (!m_entityDataCache->IsVisibleEntityVisible(entityCacheIndex) || !IsSelectableInViewport(entityId))
{
continue;
}
@@ -254,4 +262,24 @@ namespace AzToolsFramework
}
}
}
bool EditorHelpers::IsSelectableInViewport(AZ::EntityId entityId)
{
return IsSelectableAccordingToFocusMode(entityId) && IsSelectableAccordingToContainerEntities(entityId);
}
bool EditorHelpers::IsSelectableAccordingToFocusMode(AZ::EntityId entityId)
{
return m_focusModeInterface->IsInFocusSubTree(entityId);
}
bool EditorHelpers::IsSelectableAccordingToContainerEntities(AZ::EntityId entityId)
{
if (ContainerEntityInterface* containerEntityInterface = AZ::Interface<ContainerEntityInterface>::Get())
{
return !containerEntityInterface->IsUnderClosedContainerEntity(entityId);
}
return true;
}
} // namespace AzToolsFramework
@@ -58,7 +58,19 @@ namespace AzToolsFramework
AzFramework::DebugDisplayRequests& debugDisplay,
const AZStd::function<bool(AZ::EntityId)>& showIconCheck);
//! Returns whether the entityId can be selected in the viewport according
//! to the current Editor Focus Mode and Container Entity setup.
bool IsSelectableInViewport(AZ::EntityId entityId);
private:
//! Returns whether the entityId can be selected in the viewport according
//! to the current Editor Focus Mode setup.
bool IsSelectableAccordingToFocusMode(AZ::EntityId entityId);
//! Returns whether the entityId can be selected in the viewport according
//! to the current Container Entityu setup.
bool IsSelectableAccordingToContainerEntities(AZ::EntityId entityId);
const EditorVisibleEntityDataCache* m_entityDataCache = nullptr; //!< Entity Data queried by the EditorHelpers.
const FocusModeInterface* m_focusModeInterface = nullptr;
};
@@ -21,7 +21,7 @@ namespace AzToolsFramework
: m_editorHelpers(AZStd::make_unique<EditorHelpers>(entityDataCache))
, m_viewportEditorModeTracker(viewportEditorModeTracker)
{
m_viewportEditorModeTracker->ActivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Pick);
m_viewportEditorModeTracker->ActivateMode({ GetEntityContextId() }, ViewportEditorMode::Pick);
}
EditorPickEntitySelection::~EditorPickEntitySelection()
@@ -31,7 +31,7 @@ namespace AzToolsFramework
ToolsApplicationRequestBus::Broadcast(&ToolsApplicationRequests::SetEntityHighlighted, m_hoveredEntityId, false);
}
m_viewportEditorModeTracker->DeactivateMode({ /* DefaultViewportId */ }, ViewportEditorMode::Pick);
m_viewportEditorModeTracker->DeactivateMode({ GetEntityContextId() }, ViewportEditorMode::Pick);
}
// note: entityIdUnderCursor is the authoritative entityId we get each frame by querying
@@ -103,10 +103,6 @@ namespace AzToolsFramework
static const char* const ResetEntityTransformDesc = "Reset transform based on manipulator mode";
static const char* const ResetManipulatorTitle = "Reset Manipulator";
static const char* const ResetManipulatorDesc = "Reset the manipulator to recenter it on the selected entity";
static const char* const ResetTransformLocalTitle = "Reset Transform (Local)";
static const char* const ResetTransformLocalDesc = "Reset transform to local space";
static const char* const ResetTransformWorldTitle = "Reset Transform (World)";
static const char* const ResetTransformWorldDesc = "Reset transform to world space";
static const char* const EntityBoxSelectUndoRedoDesc = "Box Select Entities";
static const char* const EntityDeselectUndoRedoDesc = "Deselect Entity";
@@ -1027,7 +1023,7 @@ namespace AzToolsFramework
EditorTransformComponentSelectionRequestBus::Handler::BusConnect(entityContextId);
ToolsApplicationNotificationBus::Handler::BusConnect();
Camera::EditorCameraNotificationBus::Handler::BusConnect();
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusConnect(entityContextId);
ViewportEditorModeNotificationsBus::Handler::BusConnect(entityContextId);
EditorEntityContextNotificationBus::Handler::BusConnect();
EditorEntityVisibilityNotificationBus::Router::BusRouterConnect();
EditorEntityLockComponentNotificationBus::Router::BusRouterConnect();
@@ -1075,7 +1071,7 @@ namespace AzToolsFramework
EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect();
EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect();
EditorEntityContextNotificationBus::Handler::BusDisconnect();
ComponentModeFramework::EditorComponentModeNotificationBus::Handler::BusDisconnect();
ViewportEditorModeNotificationsBus::Handler::BusDisconnect();
Camera::EditorCameraNotificationBus::Handler::BusDisconnect();
ToolsApplicationNotificationBus::Handler::BusDisconnect();
EditorTransformComponentSelectionRequestBus::Handler::BusDisconnect();
@@ -1117,7 +1113,7 @@ namespace AzToolsFramework
});
m_boxSelect.InstallLeftMouseUp(
[this, entityBoxSelectData]()
[this, entityBoxSelectData]
{
entityBoxSelectData->m_boxSelectSelectionCommand->UpdateSelection(EntityIdVectorFromContainer(m_selectedEntityIds));
@@ -2175,7 +2171,7 @@ namespace AzToolsFramework
// lock selection
AddAction(
m_actions, { QKeySequence(Qt::Key_L) }, LockSelection, LockSelectionTitle, LockSelectionDesc,
[lockUnlock]()
[lockUnlock]
{
lockUnlock(true);
});
@@ -2183,7 +2179,7 @@ namespace AzToolsFramework
// unlock selection
AddAction(
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_L) }, UnlockSelection, LockSelectionTitle, LockSelectionDesc,
[lockUnlock]()
[lockUnlock]
{
lockUnlock(false);
});
@@ -2213,7 +2209,7 @@ namespace AzToolsFramework
// hide selection
AddAction(
m_actions, { QKeySequence(Qt::Key_H) }, HideSelection, HideSelectionTitle, HideSelectionDesc,
[showHide]()
[showHide]
{
showHide(false);
});
@@ -2221,7 +2217,7 @@ namespace AzToolsFramework
// show selection
AddAction(
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_H) }, ShowSelection, HideSelectionTitle, HideSelectionDesc,
[showHide]()
[showHide]
{
showHide(true);
});
@@ -2229,7 +2225,7 @@ namespace AzToolsFramework
// unlock all entities in the level/scene
AddAction(
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L) }, UnlockAll, UnlockAllTitle, UnlockAllDesc,
[]()
[]
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
@@ -2246,14 +2242,14 @@ namespace AzToolsFramework
// show all entities in the level/scene
AddAction(
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H) }, ShowAll, ShowAllTitle, ShowAllDesc,
[]()
[]
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
ScopedUndoBatch undoBatch(ShowAllEntitiesUndoRedoDesc);
EnumerateEditorEntities(
[](AZ::EntityId entityId)
[](const AZ::EntityId entityId)
{
ScopedUndoBatch::MarkEntityDirty(entityId);
SetEntityVisibility(entityId, true);
@@ -2263,7 +2259,7 @@ namespace AzToolsFramework
// select all entities in the level/scene
AddAction(
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_A) }, SelectAll, SelectAllTitle, SelectAllDesc,
[this]()
[this]
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
@@ -2303,7 +2299,7 @@ namespace AzToolsFramework
// invert current selection
AddAction(
m_actions, { QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I) }, InvertSelect, InvertSelectionTitle, InvertSelectionDesc,
[this]()
[this]
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
@@ -2350,17 +2346,10 @@ namespace AzToolsFramework
// duplicate selection
AddAction(
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_D) }, DuplicateSelect, DuplicateTitle, DuplicateDesc,
[]()
[]
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
// Clear Widget selection - Prevents issues caused by cloning entities while a property in the Reflected Property Editor
// is being edited.
if (QApplication::focusWidget())
{
QApplication::focusWidget()->clearFocus();
}
ScopedUndoBatch undoBatch(DuplicateUndoRedoDesc);
auto selectionCommand = AZStd::make_unique<SelectionCommand>(EntityIdList(), DuplicateUndoRedoDesc);
selectionCommand->SetParent(undoBatch.GetUndoBatch());
@@ -2375,7 +2364,7 @@ namespace AzToolsFramework
// delete selection
AddAction(
m_actions, { QKeySequence(Qt::Key_Delete) }, DeleteSelect, DeleteTitle, DeleteDesc,
[this]()
[this]
{
AZ_PROFILE_FUNCTION(AzToolsFramework);
@@ -2392,21 +2381,21 @@ namespace AzToolsFramework
AddAction(
m_actions, { QKeySequence(Qt::Key_Space) }, EditEscaspe, "", "",
[this]()
[this]
{
DeselectEntities();
});
AddAction(
m_actions, { QKeySequence(Qt::Key_P) }, EditPivot, TogglePivotTitleEditMenu, TogglePivotDesc,
[this]()
[this]
{
ToggleCenterPivotSelection();
});
AddAction(
m_actions, { QKeySequence(Qt::Key_R) }, EditReset, ResetEntityTransformTitle, ResetEntityTransformDesc,
[this]()
[this]
{
switch (m_mode)
{
@@ -2424,50 +2413,14 @@ namespace AzToolsFramework
AddAction(
m_actions, { QKeySequence(Qt::CTRL + Qt::Key_R) }, EditResetManipulator, ResetManipulatorTitle, ResetManipulatorDesc,
AZStd::bind(AZStd::mem_fn(&EditorTransformComponentSelection::DelegateClearManipulatorOverride), this));
AddAction(
m_actions, { QKeySequence(Qt::ALT + Qt::Key_R) }, EditResetLocal, ResetTransformLocalTitle, ResetTransformLocalDesc,
[this]()
[this]
{
switch (m_mode)
{
case Mode::Rotation:
ResetOrientationForSelectedEntitiesLocal();
break;
case Mode::Scale:
CopyScaleToSelectedEntitiesIndividualWorld(1.0f);
break;
case Mode::Translation:
// do nothing
break;
}
});
AddAction(
m_actions, { QKeySequence(Qt::SHIFT + Qt::Key_R) }, EditResetWorld, ResetTransformWorldTitle, ResetTransformWorldDesc,
[this]()
{
switch (m_mode)
{
case Mode::Rotation:
{
// begin an undo batch so operations inside CopyOrientation... and
// DelegateClear... are grouped into a single undo/redo
ScopedUndoBatch undoBatch{ ResetTransformWorldTitle };
CopyOrientationToSelectedEntitiesIndividual(AZ::Quaternion::CreateIdentity());
ClearManipulatorOrientationOverride();
}
break;
case Mode::Scale:
case Mode::Translation:
break;
}
DelegateClearManipulatorOverride();
});
AddAction(
m_actions, { QKeySequence(Qt::Key_U) }, ViewportUiVisible, "Toggle Viewport UI", "Hide/Show Viewport UI",
[this]()
[this]
{
SetAllViewportUiVisible(!m_viewportUiVisible);
});
@@ -3276,7 +3229,7 @@ namespace AzToolsFramework
QAction* action = menu->addAction(QObject::tr(TogglePivotTitleRightClick));
QObject::connect(
action, &QAction::triggered, action,
[this]()
[this]
{
ToggleCenterPivotSelection();
});
@@ -3707,22 +3660,30 @@ namespace AzToolsFramework
m_selectedEntityIdsAndManipulatorsDirty = true;
}
void EditorTransformComponentSelection::EnteredComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
void EditorTransformComponentSelection::OnEditorModeActivated(
[[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode)
{
SetAllViewportUiVisible(false);
if (mode == ViewportEditorMode::Component)
{
SetAllViewportUiVisible(false);
EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect();
EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect();
ToolsApplicationNotificationBus::Handler::BusDisconnect();
EditorEntityLockComponentNotificationBus::Router::BusRouterDisconnect();
EditorEntityVisibilityNotificationBus::Router::BusRouterDisconnect();
ToolsApplicationNotificationBus::Handler::BusDisconnect();
}
}
void EditorTransformComponentSelection::LeftComponentMode([[maybe_unused]] const AZStd::vector<AZ::Uuid>& componentModeTypes)
void EditorTransformComponentSelection::OnEditorModeDeactivated(
[[maybe_unused]] const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode)
{
SetAllViewportUiVisible(true);
if (mode == ViewportEditorMode::Component)
{
SetAllViewportUiVisible(true);
ToolsApplicationNotificationBus::Handler::BusConnect();
EditorEntityVisibilityNotificationBus::Router::BusRouterConnect();
EditorEntityLockComponentNotificationBus::Router::BusRouterConnect();
ToolsApplicationNotificationBus::Handler::BusConnect();
EditorEntityVisibilityNotificationBus::Router::BusRouterConnect();
EditorEntityLockComponentNotificationBus::Router::BusRouterConnect();
}
}
void EditorTransformComponentSelection::CreateEntityManipulatorDeselectCommand(ScopedUndoBatch& undoBatch)
@@ -18,7 +18,7 @@
#include <AzFramework/Viewport/CursorState.h>
#include <AzToolsFramework/API/EditorCameraBus.h>
#include <AzToolsFramework/Commands/EntityManipulatorCommand.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Manipulators/BaseManipulator.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponentBus.h>
@@ -153,7 +153,7 @@ namespace AzToolsFramework
, private EditorTransformComponentSelectionRequestBus::Handler
, private ToolsApplicationNotificationBus::Handler
, private Camera::EditorCameraNotificationBus::Handler
, private ComponentModeFramework::EditorComponentModeNotificationBus::Handler
, private ViewportEditorModeNotificationsBus::Handler
, private EditorEntityContextNotificationBus::Handler
, private EditorEntityVisibilityNotificationBus::Router
, private EditorEntityLockComponentNotificationBus::Router
@@ -286,9 +286,9 @@ namespace AzToolsFramework
// EditorContextLockComponentNotificationBus overrides ...
void OnEntityLockChanged(bool locked) override;
// EditorComponentModeNotificationBus overrides ...
void EnteredComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
void LeftComponentMode(const AZStd::vector<AZ::Uuid>& componentModeTypes) override;
// ViewportEditorModeNotificationsBus overrides ...
void OnEditorModeActivated(const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override;
void OnEditorModeDeactivated(const ViewportEditorModesInterface& editorModeState, ViewportEditorMode mode) override;
// EditorEntityContextNotificationBus overrides ...
void OnStartPlayInEditor() override;
@@ -31,8 +31,6 @@ namespace AzToolsFramework
constexpr inline AZ::Crc32 EditPivot = AZ_CRC_CE("com.o3de.action.editortransform.editpivot");
constexpr inline AZ::Crc32 EditReset = AZ_CRC_CE("com.o3de.action.editortransform.editreset");
constexpr inline AZ::Crc32 EditResetManipulator = AZ_CRC_CE("com.o3de.action.editortransform.editresetmanipulator");
constexpr inline AZ::Crc32 EditResetLocal = AZ_CRC_CE("com.o3de.action.editortransform.editresetlocal");
constexpr inline AZ::Crc32 EditResetWorld = AZ_CRC_CE("com.o3de.action.editortransform.editresetworld");
constexpr inline AZ::Crc32 ViewportUiVisible = AZ_CRC_CE("com.o3de.action.editortransform.viewportuivisible");
//@}
@@ -46,13 +46,14 @@ namespace AzToolsFramework
}
AZ::Outcome<void, AZStd::string> ViewportEditorModeTracker::ActivateMode(
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode)
const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo, ViewportEditorMode mode)
{
auto& editorModes = m_viewportEditorModesMap[viewportEditorModeInfo.m_id];
auto& editorModes = m_viewportEditorModesMap[ViewportEditorModeTrackerInfo.m_id];
if (editorModes.IsModeActive(mode))
{
return AZ::Failure(AZStd::string::format(
"Duplicate call to ActivateMode for mode '%u' on id '%i'", static_cast<AZ::u32>(mode), viewportEditorModeInfo.m_id));
"Duplicate call to ActivateMode for mode '%u' on id '%s'", static_cast<AZ::u32>(mode),
ViewportEditorModeTrackerInfo.m_id.ToString<AZStd::string>().c_str()));
}
if (const auto result = editorModes.ActivateMode(mode);
@@ -62,29 +63,30 @@ namespace AzToolsFramework
}
ViewportEditorModeNotificationsBus::Event(
viewportEditorModeInfo.m_id, &ViewportEditorModeNotificationsBus::Events::OnEditorModeActivated, editorModes, mode);
ViewportEditorModeTrackerInfo.m_id, &ViewportEditorModeNotificationsBus::Events::OnEditorModeActivated, editorModes, mode);
return AZ::Success();
}
AZ::Outcome<void, AZStd::string> ViewportEditorModeTracker::DeactivateMode(
const ViewportEditorModeInfo& viewportEditorModeInfo, ViewportEditorMode mode)
const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo, ViewportEditorMode mode)
{
ViewportEditorModes* editorModes = nullptr;
bool modeWasActive = true;
if (m_viewportEditorModesMap.count(viewportEditorModeInfo.m_id))
if (m_viewportEditorModesMap.count(ViewportEditorModeTrackerInfo.m_id))
{
editorModes = &m_viewportEditorModesMap.at(viewportEditorModeInfo.m_id);
editorModes = &m_viewportEditorModesMap.at(ViewportEditorModeTrackerInfo.m_id);
if (!editorModes->IsModeActive(mode))
{
return AZ::Failure(AZStd::string::format(
"Duplicate call to DeactivateMode for mode '%u' on id '%i'", static_cast<AZ::u32>(mode), viewportEditorModeInfo.m_id));
"Duplicate call to DeactivateMode for mode '%u' on id '%s'", static_cast<AZ::u32>(mode),
ViewportEditorModeTrackerInfo.m_id.ToString<AZStd::string>().c_str()));
}
}
else
{
modeWasActive = false;
editorModes = &m_viewportEditorModesMap[viewportEditorModeInfo.m_id];
editorModes = &m_viewportEditorModesMap[ViewportEditorModeTrackerInfo.m_id];
}
if(const auto result = editorModes->DeactivateMode(mode);
@@ -94,7 +96,7 @@ namespace AzToolsFramework
}
ViewportEditorModeNotificationsBus::Event(
viewportEditorModeInfo.m_id, &ViewportEditorModeNotificationsBus::Events::OnEditorModeDeactivated, *editorModes, mode);
ViewportEditorModeTrackerInfo.m_id, &ViewportEditorModeNotificationsBus::Events::OnEditorModeDeactivated, *editorModes, mode);
if (modeWasActive)
{
@@ -103,14 +105,14 @@ namespace AzToolsFramework
else
{
return AZ::Failure(AZStd::string::format(
"Call to DeactivateMode for mode '%u' on id '%i' without precursor call to ActivateMode", static_cast<AZ::u32>(mode),
viewportEditorModeInfo.m_id));
"Call to DeactivateMode for mode '%u' on id '%s' without precursor call to ActivateMode", static_cast<AZ::u32>(mode),
ViewportEditorModeTrackerInfo.m_id.ToString<AZStd::string>().c_str()));
}
}
const ViewportEditorModesInterface* ViewportEditorModeTracker::GetViewportEditorModes(const ViewportEditorModeInfo& viewportEditorModeInfo) const
const ViewportEditorModesInterface* ViewportEditorModeTracker::GetViewportEditorModes(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo) const
{
if (auto editorModes = m_viewportEditorModesMap.find(viewportEditorModeInfo.m_id);
if (auto editorModes = m_viewportEditorModesMap.find(ViewportEditorModeTrackerInfo.m_id);
editorModes != m_viewportEditorModesMap.end())
{
return &editorModes->second;
@@ -126,8 +128,8 @@ namespace AzToolsFramework
return m_viewportEditorModesMap.size();
}
bool ViewportEditorModeTracker::IsViewportModeTracked(const ViewportEditorModeInfo& viewportEditorModeInfo) const
bool ViewportEditorModeTracker::IsViewportModeTracked(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo) const
{
return m_viewportEditorModesMap.count(viewportEditorModeInfo.m_id) > 0;
return m_viewportEditorModesMap.count(ViewportEditorModeTrackerInfo.m_id) > 0;
}
} // namespace AzToolsFramework
@@ -42,14 +42,14 @@ namespace AzToolsFramework
{
public:
// 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;
const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeInfo& viewportEditorModeInfo) const override;
AZ::Outcome<void, AZStd::string> ActivateMode(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo, ViewportEditorMode mode) override;
AZ::Outcome<void, AZStd::string> DeactivateMode(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo, ViewportEditorMode mode) override;
const ViewportEditorModesInterface* GetViewportEditorModes(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo) const override;
size_t GetTrackedViewportCount() const override;
bool IsViewportModeTracked(const ViewportEditorModeInfo& viewportEditorModeInfo) const override;
bool IsViewportModeTracked(const ViewportEditorModeTrackerInfo& ViewportEditorModeTrackerInfo) const override;
private:
using ViewportEditorModesMap = AZStd::unordered_map<typename ViewportEditorModeInfo::IdType, ViewportEditorModes>;
ViewportEditorModesMap m_viewportEditorModesMap; //!< Editor mode state per viewport.
using ViewportEditorModesMap = AZStd::unordered_map<typename ViewportEditorModeTrackerInfo::IdType, ViewportEditorModes>;
ViewportEditorModesMap m_viewportEditorModesMap; //!< Editor mode states per tracker.
};
} // namespace AzToolsFramework
@@ -339,7 +339,7 @@ namespace AzToolsFramework::ViewportUi::Internal
{
// no background for the widget else each set of buttons/text-fields/etc would have a black box around them
SetTransparentBackground(mainWindow);
mainWindow->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
mainWindow->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
}
void ViewportUiDisplay::InitializeUiOverlay()
@@ -30,12 +30,14 @@ set(FILES
API/AssetDatabaseBus.h
API/ComponentEntityObjectBus.h
API/ComponentEntitySelectionBus.h
API/ComponentModeCollectionInterface.h
API/EditorCameraBus.h
API/EditorCameraBus.cpp
API/EditorAnimationSystemRequestBus.h
API/EditorEntityAPI.h
API/EditorLevelNotificationBus.h
API/ViewportEditorModeTrackerNotificationBus.h
API/ViewportEditorModeTrackerNotificationBus.cpp
API/EditorVegetationRequestsBus.h
API/EditorPythonConsoleBus.h
API/EditorPythonRunnerRequestsBus.h
@@ -114,6 +116,10 @@ set(FILES
Component/EditorLevelComponentAPIBus.h
Component/EditorLevelComponentAPIComponent.cpp
Component/EditorLevelComponentAPIComponent.h
ContainerEntity/ContainerEntityInterface.h
ContainerEntity/ContainerEntityNotificationBus.h
ContainerEntity/ContainerEntitySystemComponent.cpp
ContainerEntity/ContainerEntitySystemComponent.h
Editor/EditorContextMenuBus.h
Editor/EditorSettingsAPIBus.h
Entity/EditorEntityStartStatus.h
@@ -148,6 +154,8 @@ set(FILES
Entity/SliceEditorEntityOwnershipService.h
Entity/SliceEditorEntityOwnershipService.cpp
Entity/SliceEditorEntityOwnershipServiceBus.h
Entity/EntityUtilityComponent.h
Entity/EntityUtilityComponent.cpp
Fingerprinting/TypeFingerprinter.h
Fingerprinting/TypeFingerprinter.cpp
FocusMode/FocusModeInterface.h
@@ -638,13 +646,22 @@ set(FILES
Prefab/PrefabFocusHandler.cpp
Prefab/PrefabFocusInterface.h
Prefab/PrefabFocusNotificationBus.h
Prefab/PrefabFocusPublicInterface.h
Prefab/PrefabFocusUndo.h
Prefab/PrefabFocusUndo.cpp
Prefab/PrefabIdTypes.h
Prefab/PrefabLoader.h
Prefab/PrefabLoader.cpp
Prefab/PrefabLoaderInterface.h
Prefab/PrefabLoaderScriptingBus.h
Prefab/ScriptingPrefabLoader.h
Prefab/ScriptingPrefabLoader.cpp
Prefab/PrefabSystemComponent.h
Prefab/PrefabSystemComponent.cpp
Prefab/PrefabSystemComponentInterface.h
Prefab/PrefabSystemScriptingBus.h
Prefab/PrefabSystemScriptingHandler.h
Prefab/PrefabSystemScriptingHandler.cpp
Prefab/Instance/Instance.h
Prefab/Instance/Instance.cpp
Prefab/Instance/InstanceSerializer.h
@@ -667,6 +684,8 @@ set(FILES
Prefab/Instance/TemplateInstanceMapperInterface.h
Prefab/Link/Link.h
Prefab/Link/Link.cpp
Prefab/Procedural/ProceduralPrefabAsset.h
Prefab/Procedural/ProceduralPrefabAsset.cpp
Prefab/PrefabPublicHandler.h
Prefab/PrefabPublicHandler.cpp
Prefab/PrefabPublicInterface.h
@@ -131,13 +131,13 @@ namespace UnitTest
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
// 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);
if (auto fileIoBase = AZ::IO::FileIOBase::GetInstance(); fileIoBase != nullptr)
{
fileIoBase->SetAlias("@assets@", m_tempDir.GetDirectory());
fileIoBase->SetAlias("@products@", m_tempDir.GetDirectory());
}
}
@@ -37,10 +37,10 @@ namespace // anonymous
bool Search(const AzToolsFramework::AssetFileInfoList& assetList, const AZ::Data::AssetId& assetId)
{
return AZStd::find_if(assetList.m_fileInfoList.begin(), assetList.m_fileInfoList.end(),
[&](AzToolsFramework::AssetFileInfo fileInfo)
{
return fileInfo.m_assetId == assetId;
return AZStd::find_if(assetList.m_fileInfoList.begin(), assetList.m_fileInfoList.end(),
[&](AzToolsFramework::AssetFileInfo fileInfo)
{
return fileInfo.m_assetId == assetId;
});
}
}
@@ -74,11 +74,11 @@ namespace UnitTest
m_application->Start(AzFramework::Application::Descriptor());
// By default @assets@ is setup to include the platform at the end. But this test is going to
// By default @products@ is setup to include the platform at the end. But this test is going to
// loop over platforms and it will be included as part of the relative path of the file.
// So the asset folder for these tests have to point to the cache project root folder, which
// doesn't include the platform.
AZ::IO::FileIOBase::GetInstance()->SetAlias("@assets@", cacheProjectRootFolder.c_str());
AZ::IO::FileIOBase::GetInstance()->SetAlias("@products@", cacheProjectRootFolder.c_str());
for (int idx = 0; idx < s_totalAssets; idx++)
{
@@ -158,17 +158,17 @@ namespace UnitTest
m_assetRegistry->RegisterAssetDependency(assets[5], AZ::Data::ProductDependency(assets[6], 0));
m_assetRegistry->RegisterAssetDependency(assets[6], AZ::Data::ProductDependency(assets[7], 0));
// asset8 -> asset6
// asset8 -> asset6
m_assetRegistry->RegisterAssetDependency(assets[8], AZ::Data::ProductDependency(assets[6], 0));
// asset10 -> asset11
// asset10 -> asset11
m_assetRegistry->RegisterAssetDependency(assets[10], AZ::Data::ProductDependency(assets[11], 0));
// asset11 -> asset10
// asset11 -> asset10
m_assetRegistry->RegisterAssetDependency(assets[11], AZ::Data::ProductDependency(assets[10], 0));
// 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
// 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);
@@ -203,10 +203,6 @@ namespace UnitTest
const AZStd::string engroot = AZ::Test::GetEngineRootPath();
AZ::IO::FileIOBase::GetInstance()->SetAlias("@engroot@", engroot.c_str());
AZ::IO::Path assetRoot(AZ::Utils::GetProjectPath());
assetRoot /= "Cache";
AZ::IO::FileIOBase::GetInstance()->SetAlias("@root@", assetRoot.c_str());
}
void TearDown() override
@@ -219,7 +215,7 @@ namespace UnitTest
delete m_application;
}
AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
AZ::Data::AssetInfo GetAssetInfoById(const AZ::Data::AssetId& id) override
{
auto foundIter = m_assetRegistry->m_assetIdToInfo.find(id);
if (foundIter != m_assetRegistry->m_assetIdToInfo.end())
@@ -540,7 +536,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList, assets[7]));
EXPECT_TRUE(Search(assetList, assets[8]));
// Removing the android flag from the asset should still produce the same result
// Removing the android flag from the asset should still produce the same result
m_assetSeedManager->RemoveSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::PC);
@@ -564,7 +560,7 @@ namespace UnitTest
EXPECT_TRUE(Search(assetList, assets[3]));
EXPECT_TRUE(Search(assetList, assets[4]));
// Adding the android flag again to the asset
// Adding the android flag again to the asset
m_assetSeedManager->AddSeedAsset(assets[8], AzFramework::PlatformFlags::Platform_ANDROID);
assetList = m_assetSeedManager->GetDependencyList(AzFramework::PlatformId::ANDROID_ID);
@@ -624,7 +620,7 @@ namespace UnitTest
EXPECT_EQ(assetList1.m_fileInfoList[0].m_assetId, assetList2.m_fileInfoList[0].m_assetId);
EXPECT_GE(assetList2.m_fileInfoList[0].m_modificationTime, assetList1.m_fileInfoList[0].m_modificationTime); // file mod time should change
// file hash should not change
for (int idx = 0; idx < 5; idx++)
{
@@ -680,7 +676,7 @@ namespace UnitTest
m_assetSeedManager->AddSeedAsset(assets[validFileIndex], AzFramework::PlatformFlags::Platform_PC, m_assetsPath[invalidFileIndex]);
const AzFramework::AssetSeedList& oldSeedList = m_assetSeedManager->GetAssetSeedList();
for (const auto& seedInfo : oldSeedList)
{
if (seedInfo.m_assetId == assets[validFileIndex])
@@ -18,8 +18,6 @@ namespace UnitTests
{
public:
MOCK_METHOD1(GetAbsoluteAssetDatabaseLocation, bool(AZStd::string&));
MOCK_METHOD0(GetAbsoluteDevGameFolderPath, const char* ());
MOCK_METHOD0(GetAbsoluteDevRootFolderPath, const char* ());
MOCK_METHOD2(GetRelativeProductPathFromFullSourceOrProductPath, bool(const AZStd::string& fullPath, AZStd::string& relativeProductPath));
MOCK_METHOD3(GenerateRelativeSourcePath,
bool(const AZStd::string& sourcePath, AZStd::string& relativePath, AZStd::string& watchFolder));
@@ -0,0 +1,252 @@
/*
* 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 <AzFramework/Entity/BehaviorEntity.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <Entity/EntityUtilityComponent.h>
#include <ToolsComponents/TransformComponent.h>
namespace UnitTest
{
// Global variables for communicating between Lua test code and C++
AZ::EntityId g_globalEntityId = AZ::EntityId{};
AZStd::string g_globalString = "";
AzFramework::BehaviorComponentId g_globalComponentId = {};
AZStd::vector<AzToolsFramework::ComponentDetails> g_globalComponentDetails = {};
bool g_globalBool = false;
class EntityUtilityComponentTests
: public ToolsApplicationFixture
{
void InitProperties()
{
AZ::ComponentApplicationRequests* componentApplicationRequests = AZ::Interface<AZ::ComponentApplicationRequests>::Get();
ASSERT_NE(componentApplicationRequests, nullptr);
auto behaviorContext = componentApplicationRequests->GetBehaviorContext();
ASSERT_NE(behaviorContext, nullptr);
behaviorContext->Property("g_globalEntityId", BehaviorValueProperty(&g_globalEntityId));
behaviorContext->Property("g_globalString", BehaviorValueProperty(&g_globalString));
behaviorContext->Property("g_globalComponentId", BehaviorValueProperty(&g_globalComponentId));
behaviorContext->Property("g_globalBool", BehaviorValueProperty(&g_globalBool));
behaviorContext->Property("g_globalComponentDetails", BehaviorValueProperty(&g_globalComponentDetails));
g_globalEntityId = AZ::EntityId{};
g_globalString = AZStd::string{};
g_globalComponentId = AzFramework::BehaviorComponentId{};
g_globalBool = false;
g_globalComponentDetails = AZStd::vector<AzToolsFramework::ComponentDetails>{};
}
void SetUpEditorFixtureImpl() override
{
InitProperties();
}
void TearDownEditorFixtureImpl() override
{
g_globalString.set_capacity(0); // Free all memory
g_globalComponentDetails.set_capacity(0);
}
};
TEST_F(EntityUtilityComponentTests, CreateEntity)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
sc.Execute(R"LUA(
g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
my_entity = Entity(g_globalEntityId)
g_globalString = my_entity:GetName()
)LUA");
EXPECT_NE(g_globalEntityId, AZ::EntityId{});
EXPECT_STREQ(g_globalString.c_str(), "test");
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(g_globalEntityId);
ASSERT_NE(entity, nullptr);
// Test cleaning up, make sure the entity is destroyed
AzToolsFramework::EntityUtilityBus::Broadcast(&AzToolsFramework::EntityUtilityBus::Events::ResetEntityContext);
entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(g_globalEntityId);
ASSERT_EQ(entity, nullptr);
}
TEST_F(EntityUtilityComponentTests, CreateEntityEmptyName)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
sc.Execute(R"LUA(
g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("")
)LUA");
EXPECT_NE(g_globalEntityId, AZ::EntityId{});
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(g_globalEntityId);
ASSERT_NE(entity, nullptr);
}
TEST_F(EntityUtilityComponentTests, FindComponent)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
sc.Execute(R"LUA(
ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0 TransformComponent")
)LUA");
EXPECT_TRUE(g_globalComponentId.IsValid());
}
TEST_F(EntityUtilityComponentTests, InvalidComponentName)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
AZ_TEST_START_TRACE_SUPPRESSION;
sc.Execute(R"LUA(
ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "ThisIsNotAComponent-Error")
)LUA");
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(g_globalComponentId.IsValid());
}
TEST_F(EntityUtilityComponentTests, InvalidComponentId)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
AZ_TEST_START_TRACE_SUPPRESSION;
sc.Execute(R"LUA(
ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "{1234-hello-world-this-is-not-an-id}")
)LUA");
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // Should get 1 error stating the type id is not valid
EXPECT_FALSE(g_globalComponentId.IsValid());
}
TEST_F(EntityUtilityComponentTests, CreateComponent)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
sc.Execute(R"LUA(
ent_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
g_globalComponentId = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(ent_id, "ScriptEditorComponent")
)LUA");
EXPECT_TRUE(g_globalComponentId.IsValid());
}
TEST_F(EntityUtilityComponentTests, UpdateComponent)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
sc.Execute(R"LUA(
g_globalEntityId = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test")
comp_id = EntityUtilityBus.Broadcast.GetOrAddComponentByTypeName(g_globalEntityId, "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent")
json_update = [[
{
"Transform Data": { "Rotate": [0.0, 0.1, 180.0] }
}
]]
g_globalBool = EntityUtilityBus.Broadcast.UpdateComponentForEntity(g_globalEntityId, comp_id, json_update);
)LUA");
EXPECT_TRUE(g_globalBool);
EXPECT_NE(g_globalEntityId, AZ::EntityId(AZ::EntityId::InvalidEntityId));
AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(g_globalEntityId);
auto* transformComponent = entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
ASSERT_NE(transformComponent, nullptr);
AZ::Vector3 localRotation = transformComponent->GetLocalRotationQuaternion().GetEulerDegrees();
EXPECT_EQ(localRotation, AZ::Vector3(.0f, 0.1f, 180.0f));
}
TEST_F(EntityUtilityComponentTests, GetComponentJson)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
sc.Execute(R"LUA(
g_globalString = EntityUtilityBus.Broadcast.GetComponentDefaultJson("ScriptEditorComponent")
)LUA");
EXPECT_STRNE(g_globalString.c_str(), "");
}
TEST_F(EntityUtilityComponentTests, GetComponentJsonDoesNotExist)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
AZ_TEST_START_TRACE_SUPPRESSION;
sc.Execute(R"LUA(
g_globalString = EntityUtilityBus.Broadcast.GetComponentDefaultJson("404")
)LUA");
AZ_TEST_STOP_TRACE_SUPPRESSION(1); // 1 error: Failed to find component id for type name 404
EXPECT_STREQ(g_globalString.c_str(), "");
}
TEST_F(EntityUtilityComponentTests, SearchComponents)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
sc.Execute(R"LUA(
g_globalComponentDetails = EntityUtilityBus.Broadcast.FindMatchingComponents("Transform*")
)LUA");
// There should be 2 transform components
EXPECT_EQ(g_globalComponentDetails.size(), 2);
}
TEST_F(EntityUtilityComponentTests, SearchComponentsNotFound)
{
AZ::ScriptContext sc;
auto behaviorContext = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->GetBehaviorContext();
sc.BindTo(behaviorContext);
sc.Execute(R"LUA(
g_globalComponentDetails = EntityUtilityBus.Broadcast.FindMatchingComponents("404")
)LUA");
EXPECT_EQ(g_globalComponentDetails.size(), 0);
}
}
@@ -181,8 +181,8 @@ namespace UnitTest
const char* dir = m_componentApplication->GetExecutableFolder();
m_localFileIO.SetAlias("@assets@", dir);
m_localFileIO.SetAlias("@devassets@", dir);
m_localFileIO.SetAlias("@products@", dir);
m_localFileIO.SetAlias("@projectroot@", dir);
}
void Destroy()

Some files were not shown because too many files have changed in this diff Show More