Added API for finding components based on required/incompatible services.

Signed-off-by: Chris Galvan <chgalvan@amazon.com>
This commit is contained in:
Chris Galvan
2021-10-22 10:30:48 -05:00
parent 9ce6f43a22
commit 95c2ee0e9d
8 changed files with 112 additions and 76 deletions
@@ -36,6 +36,10 @@ namespace AzToolsFramework
//! the entity type.
virtual AZStd::vector<AZ::Uuid> FindComponentTypeIdsByEntityType(const AZStd::vector<AZStd::string>& componentTypeNames, EntityType entityType) = 0;
//! Return a list of type ids for components that match the required services filter,
//! and don't conflict with any of the incompatible services filter
virtual AZStd::vector<AZ::Uuid> FindComponentTypeIdsByService(const AZStd::vector<AZ::ComponentServiceType>& serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter) = 0;
//! Finds the component names from their type ids
virtual AZStd::vector<AZStd::string> FindComponentTypeNames(const AZ::ComponentTypeList& componentTypeIds) = 0;
@@ -15,6 +15,7 @@
#include <AzToolsFramework/ToolsComponents/EditorDisabledCompositionBus.h>
#include <AzToolsFramework/ToolsComponents/EditorPendingCompositionBus.h>
#include <AzToolsFramework/Entity/EditorEntityActionComponent.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
@@ -75,6 +76,7 @@ namespace AzToolsFramework
serializeContext->Class<EditorComponentAPIComponent, AZ::Component>();
serializeContext->RegisterGenericType<AZStd::vector<AZ::EntityComponentIdPair>>();
serializeContext->RegisterGenericType<AZStd::vector<AZ::ComponentServiceType>>();
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
@@ -99,6 +101,7 @@ namespace AzToolsFramework
->Attribute(AZ::Script::Attributes::Module, "editor")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Event("FindComponentTypeIdsByEntityType", &EditorComponentAPIRequests::FindComponentTypeIdsByEntityType)
->Event("FindComponentTypeIdsByService", &EditorComponentAPIRequests::FindComponentTypeIdsByService)
->Event("FindComponentTypeNames", &EditorComponentAPIRequests::FindComponentTypeNames)
->Event("BuildComponentTypeNameListByEntityType", &EditorComponentAPIRequests::BuildComponentTypeNameListByEntityType)
->Event("AddComponentsOfType", &EditorComponentAPIRequests::AddComponentsOfType)
@@ -216,6 +219,33 @@ namespace AzToolsFramework
return foundTypeIds;
}
AZStd::vector<AZ::Uuid> EditorComponentAPIComponent::FindComponentTypeIdsByService(const AZStd::vector<AZ::ComponentServiceType>& serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter)
{
AZStd::vector<AZ::Uuid> foundTypeIds;
m_serializeContext->EnumerateDerived<AZ::Component>(
[&foundTypeIds, serviceFilter, incompatibleServiceFilter](const AZ::SerializeContext::ClassData* componentClass, const AZ::Uuid& knownType) -> bool
{
AZ_UNUSED(knownType);
if (componentClass->m_editData)
{
// If none of the required services are offered by this component, or the component
// can not be added by the user, skip to the next component
if (!OffersRequiredServices(componentClass, serviceFilter, incompatibleServiceFilter))
{
return true;
}
foundTypeIds.push_back(componentClass->m_typeId);
}
return true;
});
return foundTypeIds;
}
AZStd::vector<AZStd::string> EditorComponentAPIComponent::FindComponentTypeNames(const AZ::ComponentTypeList& componentTypeIds)
{
AZStd::vector<AZStd::string> foundTypeNames;
@@ -35,6 +35,7 @@ namespace AzToolsFramework
// EditorComponentAPIBus ...
AZStd::vector<AZ::Uuid> FindComponentTypeIdsByEntityType(const AZStd::vector<AZStd::string>& componentTypeNames, EditorComponentAPIRequests::EntityType entityType) override;
AZStd::vector<AZ::Uuid> FindComponentTypeIdsByService(const AZStd::vector<AZ::ComponentServiceType>& serviceFilter, const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter) override;
AZStd::vector<AZStd::string> FindComponentTypeNames(const AZ::ComponentTypeList& componentTypeIds) override;
AZStd::vector<AZStd::string> BuildComponentTypeNameListByEntityType(EditorComponentAPIRequests::EntityType entityType) override;
@@ -271,6 +271,68 @@ namespace AzToolsFramework
return editorComponentBaseComponent;
}
bool OffersRequiredServices(
const AZ::SerializeContext::ClassData* componentClass,
const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter
)
{
AZ_Assert(componentClass, "Component class must not be null");
if (!componentClass)
{
return false;
}
AZ::ComponentDescriptor* componentDescriptor = nullptr;
AZ::ComponentDescriptorBus::EventResult(
componentDescriptor, componentClass->m_typeId, &AZ::ComponentDescriptor::GetDescriptor);
if (!componentDescriptor)
{
return false;
}
// If no services are provided, this function returns true
if (serviceFilter.empty())
{
return true;
}
AZ::ComponentDescriptor::DependencyArrayType providedServices;
componentDescriptor->GetProvidedServices(providedServices, nullptr);
//reject this component if it does not offer any of the required services
if (AZStd::find_first_of(
providedServices.begin(),
providedServices.end(),
serviceFilter.begin(),
serviceFilter.end()) == providedServices.end())
{
return false;
}
//reject this component if it does offer any of the incompatible services
if (AZStd::find_first_of(
providedServices.begin(),
providedServices.end(),
incompatibleServiceFilter.begin(),
incompatibleServiceFilter.end()) != providedServices.end())
{
return false;
}
return true;
}
bool OffersRequiredServices(
const AZ::SerializeContext::ClassData* componentClass,
const AZStd::vector<AZ::ComponentServiceType>& serviceFilter
)
{
const AZStd::vector<AZ::ComponentServiceType> incompatibleServices;
return OffersRequiredServices(componentClass, serviceFilter, incompatibleServices);
}
bool ShouldInspectorShowComponent(const AZ::Component* component)
{
if (!component)
@@ -105,6 +105,16 @@ namespace AzToolsFramework
AZ::ComponentDescriptor* GetComponentDescriptor(const AZ::Component* component);
Components::EditorComponentDescriptor* GetEditorComponentDescriptor(const AZ::Component* component);
Components::EditorComponentBase* GetEditorComponent(AZ::Component* component);
// Returns true if the given component provides at least one of the services specified or no services are provided
bool OffersRequiredServices(
const AZ::SerializeContext::ClassData* componentClass,
const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter
);
bool OffersRequiredServices(
const AZ::SerializeContext::ClassData* componentClass,
const AZStd::vector<AZ::ComponentServiceType>& serviceFilter
);
/// Return true if the editor should show this component to users,
/// false if the component should be hidden from users.
@@ -12,6 +12,7 @@
#include <AzCore/Component/Component.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
#include <AzToolsFramework/UI/SearchWidget/SearchCriteriaWidget.hxx>
AZ_POP_DISABLE_WARNING
@@ -20,67 +21,6 @@ namespace AzToolsFramework
{
namespace ComponentPaletteUtil
{
bool OffersRequiredServices(
const AZ::SerializeContext::ClassData* componentClass,
const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter
)
{
AZ_Assert(componentClass, "Component class must not be null");
if (!componentClass)
{
return false;
}
AZ::ComponentDescriptor* componentDescriptor = nullptr;
EBUS_EVENT_ID_RESULT(componentDescriptor, componentClass->m_typeId, AZ::ComponentDescriptorBus, GetDescriptor);
if (!componentDescriptor)
{
return false;
}
// If no services are provided, this function returns true
if (serviceFilter.empty())
{
return true;
}
AZ::ComponentDescriptor::DependencyArrayType providedServices;
componentDescriptor->GetProvidedServices(providedServices, nullptr);
//reject this component if it does not offer any of the required services
if (AZStd::find_first_of(
providedServices.begin(),
providedServices.end(),
serviceFilter.begin(),
serviceFilter.end()) == providedServices.end())
{
return false;
}
//reject this component if it does offer any of the incompatible services
if (AZStd::find_first_of(
providedServices.begin(),
providedServices.end(),
incompatibleServiceFilter.begin(),
incompatibleServiceFilter.end()) != providedServices.end())
{
return false;
}
return true;
}
bool OffersRequiredServices(
const AZ::SerializeContext::ClassData* componentClass,
const AZStd::vector<AZ::ComponentServiceType>& serviceFilter
)
{
const AZStd::vector<AZ::ComponentServiceType> incompatibleServices;
return OffersRequiredServices(componentClass, serviceFilter, incompatibleServices);
}
bool IsAddableByUser(const AZ::SerializeContext::ClassData* componentClass)
{
AZ_Assert(componentClass, "component class must not be null");
@@ -26,18 +26,6 @@ namespace AzToolsFramework
using ComponentIconTable = AZStd::map<const AZ::SerializeContext::ClassData*, QString>;
// Returns true if the given component provides at least one of the services specified or no services are provided
bool OffersRequiredServices(
const AZ::SerializeContext::ClassData* componentClass,
const AZStd::vector<AZ::ComponentServiceType>& serviceFilter,
const AZStd::vector<AZ::ComponentServiceType>& incompatibleServiceFilter
);
bool OffersRequiredServices(
const AZ::SerializeContext::ClassData* componentClass,
const AZStd::vector<AZ::ComponentServiceType>& serviceFilter
);
// Returns true if the given component is addable by the user
bool IsAddableByUser(const AZ::SerializeContext::ClassData* componentClass);
@@ -22,6 +22,7 @@
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/UnitTest/ToolsTestApplication.h>
// Inspector Test Includes
@@ -313,17 +314,17 @@ namespace UnitTest
AZ_TEST_ASSERT(testComponent1_ProvidedServices.size() == 1);
const AZ::SerializeContext::ClassData* testComponent1_ClassData = context->FindClassData(testComponent1_typeId);
EXPECT_TRUE(AzToolsFramework::ComponentPaletteUtil::OffersRequiredServices(testComponent1_ClassData, testComponent1_ProvidedServices));
EXPECT_TRUE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, testComponent1_ProvidedServices));
// Verify that OffersRequiredServices returns when given services provided by a different component
AZ::ComponentDescriptor::DependencyArrayType testComponent2_ProvidedServices;
Inspector_TestComponent2::GetProvidedServices(testComponent2_ProvidedServices);
AZ_TEST_ASSERT(testComponent2_ProvidedServices.size() == 1);
AZ_TEST_ASSERT(testComponent1_ProvidedServices != testComponent2_ProvidedServices);
EXPECT_FALSE(AzToolsFramework::ComponentPaletteUtil::OffersRequiredServices(testComponent1_ClassData, testComponent2_ProvidedServices));
EXPECT_FALSE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, testComponent2_ProvidedServices));
// verify that OffersRequiredServices returns true when provided with an empty list of services
EXPECT_TRUE(AzToolsFramework::ComponentPaletteUtil::OffersRequiredServices(testComponent1_ClassData, AZ::ComponentDescriptor::DependencyArrayType()));
EXPECT_TRUE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, AZ::ComponentDescriptor::DependencyArrayType()));
//////////////////////////////////////////////////////////////////////////
// TEST IsAddableByUser()