git mv Code\Sandbox\Plugins Code/Editor/Plugins
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ComponentEntityEditorPlugin_precompiled.h"
|
||||
|
||||
#include "CategoriesList.h"
|
||||
|
||||
ComponentCategoryList::ComponentCategoryList(QWidget* parent /*= nullptr*/)
|
||||
: QTreeWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void ComponentCategoryList::Init()
|
||||
{
|
||||
setColumnCount(1);
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setDragDropMode(QAbstractItemView::DragDropMode::DragOnly);
|
||||
setDragEnabled(true);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
setAllColumnsShowFocus(true);
|
||||
setStyleSheet("QTreeWidget { selection-background-color: rgba(255,255,255,0.2); }");
|
||||
|
||||
QStringList headers;
|
||||
headers << tr("Categories");
|
||||
setHeaderLabels(headers);
|
||||
|
||||
const QString parentCategoryIconPath = QString("Icons/PropertyEditor/Browse_on.png");
|
||||
const QString categoryIconPath = QString("Icons/PropertyEditor/Browse.png");
|
||||
|
||||
QTreeWidgetItem* allCategory = new QTreeWidgetItem(this);
|
||||
allCategory->setText(0, "All");
|
||||
allCategory->setIcon(0, QIcon(categoryIconPath));
|
||||
|
||||
// Need this briefly to collect the list of available categories.
|
||||
ComponentDataModel dataModel(this);
|
||||
for (const auto& cat : dataModel.GetCategories())
|
||||
{
|
||||
QString categoryString = QString(cat.c_str());
|
||||
QStringList categories = categoryString.split('/', Qt::SkipEmptyParts);
|
||||
|
||||
QTreeWidgetItem* parent = nullptr;
|
||||
QTreeWidgetItem* categoryWidget = nullptr;
|
||||
|
||||
for (const auto& categoryName : categories)
|
||||
{
|
||||
if (parent)
|
||||
{
|
||||
categoryWidget = new QTreeWidgetItem(parent);
|
||||
categoryWidget->setIcon(0, QIcon(categoryIconPath));
|
||||
|
||||
// Store the full category path in a user role because we'll need it to locate the actual category
|
||||
categoryWidget->setData(0, Qt::UserRole, QVariant::fromValue(categoryString));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto existingCategory = findItems(categoryName, Qt::MatchExactly);
|
||||
if (existingCategory.empty())
|
||||
{
|
||||
categoryWidget = new QTreeWidgetItem(this);
|
||||
categoryWidget->setIcon(0, QIcon(parentCategoryIconPath));
|
||||
}
|
||||
else
|
||||
{
|
||||
categoryWidget = static_cast<QTreeWidgetItem*>(existingCategory.first());
|
||||
categoryWidget->setIcon(0, QIcon(parentCategoryIconPath));
|
||||
}
|
||||
}
|
||||
|
||||
parent = categoryWidget;
|
||||
|
||||
categoryWidget->setText(0, categoryName);
|
||||
}
|
||||
}
|
||||
|
||||
expandAll();
|
||||
|
||||
connect(this, &QTreeWidget::itemClicked, this, &ComponentCategoryList::OnItemClicked);
|
||||
}
|
||||
|
||||
void ComponentCategoryList::OnItemClicked(QTreeWidgetItem* item, int /*column*/)
|
||||
{
|
||||
QVariant userData = item->data(0, Qt::UserRole);
|
||||
if (userData.isValid())
|
||||
{
|
||||
// Send in the full category path, not just the child category name
|
||||
emit OnCategoryChange(userData.value<QString>().toStdString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
emit OnCategoryChange(item->text(0).toStdString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#include <UI/ComponentPalette/moc_CategoriesList.cpp>
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "ComponentDataModel.h"
|
||||
#include <QTreeWidget>
|
||||
#endif
|
||||
|
||||
//! ComponentCategoryList
|
||||
//! Provides a list of all reflected categories that users can select for quick
|
||||
//! filtering the filtered component list.
|
||||
class ComponentCategoryList : public QTreeWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(ComponentCategoryList, AZ::SystemAllocator, 0);
|
||||
|
||||
explicit ComponentCategoryList(QWidget* parent = nullptr);
|
||||
|
||||
void Init();
|
||||
|
||||
Q_SIGNALS:
|
||||
void OnCategoryChange(const char* category);
|
||||
|
||||
protected:
|
||||
|
||||
// Will emit OnCategoryChange signal
|
||||
void OnItemClicked(QTreeWidgetItem* item, int column);
|
||||
|
||||
};
|
||||
+548
@@ -0,0 +1,548 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ComponentEntityEditorPlugin_precompiled.h"
|
||||
|
||||
#include "ComponentDataModel.h"
|
||||
|
||||
#include "Include/IObjectManager.h"
|
||||
#include "Objects/SelectionGroup.h"
|
||||
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#include <AzToolsFramework/Commands/EntityStateCommand.h>
|
||||
#include <AzToolsFramework/ToolsComponents/TransformComponent.h>
|
||||
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
|
||||
|
||||
#include <Editor/IEditor.h>
|
||||
#include <Editor/Viewport.h>
|
||||
#include <Editor/ViewManager.h>
|
||||
#include <CryCommon/MathConversion.h>
|
||||
|
||||
#include <AzQtComponents/DragAndDrop/ViewportDragAndDrop.h>
|
||||
|
||||
#include <QMimeData>
|
||||
|
||||
namespace
|
||||
{
|
||||
// This is a helper function that given an object that derives from QAbstractItemModel,
|
||||
// it will request the model's "ClassDataRole" class data for an entry and use that
|
||||
// information to create a new entity with the selected components.
|
||||
AZ::EntityId CreateEntityFromSelection(const QModelIndexList& selection, QAbstractItemModel* model)
|
||||
{
|
||||
AZ::Vector3 position = AZ::Vector3::CreateZero();
|
||||
CViewport *view = GetIEditor()->GetViewManager()->GetGameViewport();
|
||||
int width, height;
|
||||
view->GetDimensions(&width, &height);
|
||||
position = LYVec3ToAZVec3(view->ViewToWorld(QPoint(width / 2, height / 2)));
|
||||
|
||||
AZ::EntityId newEntityId;
|
||||
EBUS_EVENT_RESULT(newEntityId, AzToolsFramework::EditorRequests::Bus, CreateNewEntityAtPosition, position, AZ::EntityId());
|
||||
if (newEntityId.IsValid())
|
||||
{
|
||||
// Add all the selected components.
|
||||
AZ::ComponentTypeList componentsToAdd;
|
||||
for (auto index : selection)
|
||||
{
|
||||
// We only need to consider the first column, it's important that the data() function that
|
||||
// returns ComponentDataModel::ClassDataRole also does so for the first column.
|
||||
if (index.column() != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QVariant classDataVariant = model->data(index, ComponentDataModel::ClassDataRole);
|
||||
if (classDataVariant.isValid())
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* classData = reinterpret_cast<const AZ::SerializeContext::ClassData*>(classDataVariant.value<void*>());
|
||||
componentsToAdd.push_back(classData->m_typeId);
|
||||
}
|
||||
}
|
||||
|
||||
AzToolsFramework::EntityCompositionRequestBus::Broadcast(&AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, AzToolsFramework::EntityIdList{ newEntityId }, componentsToAdd);
|
||||
|
||||
return newEntityId;
|
||||
}
|
||||
|
||||
return AZ::EntityId();
|
||||
}
|
||||
}
|
||||
|
||||
namespace ComponentDataUtilities
|
||||
{
|
||||
// This is a helper function to add the specified components to the selected entities, it relies on the provided
|
||||
// QAbstractItemModel to determine the appropriate ClassData to use to create the components (given that some widgets
|
||||
// may provide proxy models that alter the order).
|
||||
void AddComponentsToSelectedEntities(const QModelIndexList& selectedComponents, QAbstractItemModel* model)
|
||||
{
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
|
||||
if (selectedEntities.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Add all the selected components.
|
||||
AZ::ComponentTypeList componentsToAdd;
|
||||
for (auto index : selectedComponents)
|
||||
{
|
||||
// We only need to consider the first column, it's important that the data() function that
|
||||
// returns ComponentDataModel::ClassDataRole also does so for the first column.
|
||||
if (index.column() != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QVariant classDataVariant = model->data(index, ComponentDataModel::ClassDataRole);
|
||||
if (classDataVariant.isValid())
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* classData = reinterpret_cast<const AZ::SerializeContext::ClassData*>(classDataVariant.value<void*>());
|
||||
componentsToAdd.push_back(classData->m_typeId);
|
||||
}
|
||||
}
|
||||
|
||||
AzToolsFramework::EntityCompositionRequestBus::Broadcast(&AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, selectedEntities, componentsToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ComponentDataModel
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ComponentDataModel::ComponentDataModel(QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
|
||||
|
||||
serializeContext->EnumerateDerived<AZ::Component>([this](const AZ::SerializeContext::ClassData* classData, const AZ::Uuid&) -> bool
|
||||
{
|
||||
bool allowed = false;
|
||||
bool hidden = false;
|
||||
AZStd::string category = "Miscellaneous";
|
||||
|
||||
if (classData->m_editData)
|
||||
{
|
||||
for (const AZ::Edit::ElementData& element : classData->m_editData->m_elements)
|
||||
{
|
||||
if (element.m_elementId == AZ::Edit::ClassElements::EditorData)
|
||||
{
|
||||
AZStd::string iconPath;
|
||||
EBUS_EVENT_RESULT(iconPath, AzToolsFramework::EditorRequests::Bus, GetComponentEditorIcon, classData->m_typeId, nullptr);
|
||||
if (!iconPath.empty())
|
||||
{
|
||||
m_componentIcons[classData->m_typeId] = QIcon(iconPath.c_str());
|
||||
}
|
||||
|
||||
for (const AZ::Edit::AttributePair& attribPair : element.m_attributes)
|
||||
{
|
||||
if (attribPair.first == AZ::Edit::Attributes::AppearsInAddComponentMenu)
|
||||
{
|
||||
if (auto data = azdynamic_cast<AZ::Edit::AttributeData<AZ::Crc32>*>(attribPair.second))
|
||||
{
|
||||
if (data->Get(nullptr) == AZ_CRC("Game"))
|
||||
{
|
||||
allowed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attribPair.first == AZ::Edit::Attributes::AddableByUser)
|
||||
{
|
||||
// skip this component if user is not allowed to add it directly
|
||||
if (auto data = azdynamic_cast<AZ::Edit::AttributeData<bool>*>(attribPair.second))
|
||||
{
|
||||
if (!data->Get(nullptr))
|
||||
{
|
||||
hidden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attribPair.first == AZ::Edit::Attributes::Category)
|
||||
{
|
||||
if (auto data = azdynamic_cast<AZ::Edit::AttributeData<const char*>*>(attribPair.second))
|
||||
{
|
||||
category = data->Get(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowed && !hidden)
|
||||
{
|
||||
m_componentList.push_back(classData);
|
||||
m_componentMap[category].push_back(classData);
|
||||
m_categories.insert(category);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// we'd like viewport events
|
||||
AzQtComponents::DragAndDropEventsBus::Handler::BusConnect(AzQtComponents::DragAndDropContexts::EditorViewport);
|
||||
}
|
||||
|
||||
ComponentDataModel::~ComponentDataModel()
|
||||
{
|
||||
AzQtComponents::DragAndDropEventsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
Qt::ItemFlags ComponentDataModel::flags([[maybe_unused]] const QModelIndex &index) const
|
||||
{
|
||||
return Qt::ItemFlags(
|
||||
Qt::ItemIsEnabled |
|
||||
Qt::ItemIsDragEnabled |
|
||||
Qt::ItemIsDropEnabled |
|
||||
Qt::ItemIsSelectable);
|
||||
}
|
||||
|
||||
const AZ::SerializeContext::ClassData* ComponentDataModel::GetClassData(const QModelIndex& index) const
|
||||
{
|
||||
int row = index.row();
|
||||
if (row < 0 || row >= m_componentList.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_componentList[row];
|
||||
}
|
||||
|
||||
const char* ComponentDataModel::GetCategory(const AZ::SerializeContext::ClassData* classData)
|
||||
{
|
||||
if (classData)
|
||||
{
|
||||
if (auto editorDataElement = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
|
||||
{
|
||||
if (auto categoryAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::Category))
|
||||
{
|
||||
if (auto categoryData = azdynamic_cast<AZ::Edit::AttributeData<const char*>*>(categoryAttribute))
|
||||
{
|
||||
const char* result = categoryData->Get(nullptr);
|
||||
if (result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
QModelIndex ComponentDataModel::index(int row, int column, const QModelIndex &parent /*= QModelIndex()*/) const
|
||||
{
|
||||
if (row >= rowCount(parent) || column >= columnCount(parent))
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
return createIndex(row, column, (void*)(m_componentList[row]));
|
||||
}
|
||||
|
||||
QModelIndex ComponentDataModel::parent([[maybe_unused]] const QModelIndex &child) const
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int ComponentDataModel::rowCount([[maybe_unused]] const QModelIndex &parent /*= QModelIndex()*/) const
|
||||
{
|
||||
return m_componentList.size();
|
||||
}
|
||||
|
||||
int ComponentDataModel::columnCount([[maybe_unused]] const QModelIndex &parent /*= QModelIndex()*/) const
|
||||
{
|
||||
return ColumnIndex::Count;
|
||||
}
|
||||
|
||||
QVariant ComponentDataModel::data(const QModelIndex &index, int role /*= Qt::DisplayRole*/) const
|
||||
{
|
||||
if (index.isValid())
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* classData = m_componentList[index.row()];
|
||||
if (!classData)
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case ClassDataRole:
|
||||
if (index.column() == 0) // Only get data for one column
|
||||
{
|
||||
return QVariant::fromValue<void*>(reinterpret_cast<void*>(const_cast<AZ::SerializeContext::ClassData*>(classData)));
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
if (index.column() == ColumnIndex::Name)
|
||||
{
|
||||
return QVariant(classData->m_editData->m_name);
|
||||
}
|
||||
else
|
||||
if (index.column() == ColumnIndex::Category)
|
||||
{
|
||||
if (auto editorDataElement = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
|
||||
{
|
||||
if (auto categoryAttribute = editorDataElement->FindAttribute(AZ::Edit::Attributes::Category))
|
||||
{
|
||||
if (auto categoryData = azdynamic_cast<const AZ::Edit::AttributeData<const char*>*>(categoryAttribute))
|
||||
{
|
||||
return QVariant(categoryData->Get(nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
{
|
||||
return QVariant(classData->m_editData->m_description);
|
||||
}
|
||||
|
||||
case Qt::DecorationRole:
|
||||
{
|
||||
if (index.column() == ColumnIndex::Icon)
|
||||
{
|
||||
auto iconIterator = m_componentIcons.find(classData->m_typeId);
|
||||
if (iconIterator != m_componentIcons.end())
|
||||
{
|
||||
return iconIterator->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QMimeData* ComponentDataModel::mimeData(const QModelIndexList& indices) const
|
||||
{
|
||||
QModelIndexList list;
|
||||
|
||||
// Filter out columns we are not interested in.
|
||||
for (const QModelIndex& index : indices)
|
||||
{
|
||||
if (index.column() == 0)
|
||||
{
|
||||
list.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::vector<const AZ::SerializeContext::ClassData*> sortedList;
|
||||
for (QModelIndex index : list)
|
||||
{
|
||||
QVariant classDataVariant = index.data(ComponentDataModel::ClassDataRole);
|
||||
if (classDataVariant.isValid())
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* classData = reinterpret_cast<const AZ::SerializeContext::ClassData*>(classDataVariant.value<void*>());
|
||||
sortedList.push_back(classData);
|
||||
}
|
||||
}
|
||||
|
||||
QMimeData* mimeData = nullptr;
|
||||
if (!sortedList.empty())
|
||||
{
|
||||
mimeData = AzToolsFramework::ComponentTypeMimeData::Create(sortedList).release();
|
||||
}
|
||||
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
bool ComponentDataModel::CanAcceptDragAndDropEvent(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context) const
|
||||
{
|
||||
using namespace AzToolsFramework;
|
||||
using namespace AzQtComponents;
|
||||
|
||||
// if a listener with a higher priority already claimed this event, do not touch it.
|
||||
if ((!event) || (event->isAccepted()) || (!event->mimeData()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewportDragContext* contextVP = azrtti_cast<ViewportDragContext*>(&context);
|
||||
if (!contextVP)
|
||||
{
|
||||
// not a viewport event. This is for some other GUI such as the main window itself.
|
||||
return false;
|
||||
}
|
||||
|
||||
AZStd::vector<const AZ::SerializeContext::ClassData*> componentClassDataList;
|
||||
return AzToolsFramework::ComponentTypeMimeData::Get(event->mimeData(), componentClassDataList);
|
||||
}
|
||||
|
||||
void ComponentDataModel::DragEnter(QDragEnterEvent* event, AzQtComponents::DragAndDropContextBase& context)
|
||||
{
|
||||
if (CanAcceptDragAndDropEvent(event, context))
|
||||
{
|
||||
event->setDropAction(Qt::CopyAction);
|
||||
event->setAccepted(true);
|
||||
// opportunities to show special highlights, or ghosted entities or previews here.
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentDataModel::DragMove(QDragMoveEvent* event, AzQtComponents::DragAndDropContextBase& context)
|
||||
{
|
||||
if (CanAcceptDragAndDropEvent(event, context))
|
||||
{
|
||||
event->setDropAction(Qt::CopyAction);
|
||||
event->setAccepted(true);
|
||||
// opportunities to update special highlights, or ghosted entities or previews here.
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentDataModel::DragLeave(QDragLeaveEvent* /*event*/)
|
||||
{
|
||||
// opportunities to remove ghosted entities or previews here.
|
||||
}
|
||||
|
||||
void ComponentDataModel::Drop(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context)
|
||||
{
|
||||
using namespace AzToolsFramework;
|
||||
using namespace AzQtComponents;
|
||||
|
||||
// ALWAYS CHECK - you are not the only one connected to this bus, and someone else may have already
|
||||
// handled the event or accepted the drop - it might not contain types relevant to you.
|
||||
// you still get informed about the drop event in case you did some stuff in your gui and need to clean it up.
|
||||
if (!CanAcceptDragAndDropEvent(event, context))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// note that the above call already checks all the pointers such as event, or whether context is a VP context, mimetype, etc
|
||||
ViewportDragContext* contextVP = azrtti_cast<ViewportDragContext*>(&context);
|
||||
|
||||
// we don't get given this action by Qt unless we already returned accepted from one of the other ones (such as drag move of drag enter)
|
||||
event->setDropAction(Qt::CopyAction);
|
||||
event->setAccepted(true);
|
||||
|
||||
AzToolsFramework::ScopedUndoBatch undo("Create entity from components");
|
||||
const AZStd::string name = AZStd::string::format("Entity%d", GetIEditor()->GetObjectManager()->GetObjectCount());
|
||||
|
||||
AZ::Entity* newEntity = aznew AZ::Entity(name.c_str());
|
||||
if (newEntity)
|
||||
{
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequests::AddRequiredComponents, *newEntity);
|
||||
auto* transformComponent = newEntity->FindComponent<AzToolsFramework::Components::TransformComponent>();
|
||||
if (transformComponent)
|
||||
{
|
||||
transformComponent->SetWorldTM(AZ::Transform::CreateTranslation(contextVP->m_hitLocation));
|
||||
}
|
||||
|
||||
// Add the entity to the editor context, which activates it and creates the sandbox object.
|
||||
AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequests::AddEditorEntity, newEntity);
|
||||
|
||||
// Prepare undo command last so it captures the final state of the entity.
|
||||
AzToolsFramework::EntityCreateCommand* command = aznew AzToolsFramework::EntityCreateCommand(static_cast<AZ::u64>(newEntity->GetId()));
|
||||
command->Capture(newEntity);
|
||||
command->SetParent(undo.GetUndoBatch());
|
||||
|
||||
// Only need to add components to the new entity
|
||||
AzToolsFramework::EntityIdList entities = { newEntity->GetId() };
|
||||
|
||||
AZStd::vector<const AZ::SerializeContext::ClassData*> componentClassDataList;
|
||||
AzToolsFramework::ComponentTypeMimeData::Get(event->mimeData(), componentClassDataList);
|
||||
|
||||
AZ::ComponentTypeList componentsToAdd;
|
||||
for (auto classData : componentClassDataList)
|
||||
{
|
||||
if (!classData)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
componentsToAdd.push_back(classData->m_typeId);
|
||||
}
|
||||
|
||||
AzToolsFramework::EntityCompositionRequests::AddComponentsOutcome addedComponentsResult = AZ::Failure(AZStd::string("Failed to call AddComponentsToEntities on EntityCompositionRequestBus"));
|
||||
AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(addedComponentsResult, &AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities, entities, componentsToAdd);
|
||||
|
||||
ToolsApplicationRequests::Bus::Broadcast(&ToolsApplicationRequests::AddDirtyEntity, newEntity->GetId());
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(&AzToolsFramework::ToolsApplicationRequests::SetSelectedEntities, entities);
|
||||
}
|
||||
}
|
||||
|
||||
AZ::EntityId ComponentDataProxyModel::NewEntityFromSelection(const QModelIndexList& selection)
|
||||
{
|
||||
return CreateEntityFromSelection(selection, this);
|
||||
}
|
||||
|
||||
AZ::EntityId ComponentDataModel::NewEntityFromSelection(const QModelIndexList& selection)
|
||||
{
|
||||
return CreateEntityFromSelection(selection, this);
|
||||
}
|
||||
|
||||
bool ComponentDataProxyModel::filterAcceptsRow(int sourceRow, [[maybe_unused]] const QModelIndex &sourceParent) const
|
||||
{
|
||||
if (m_selectedCategory.empty() && !filterRegExp().isValid())
|
||||
return true;
|
||||
|
||||
ComponentDataModel* dataModel = static_cast<ComponentDataModel*>(sourceModel());
|
||||
if (sourceRow < 0 || sourceRow >= dataModel->GetComponents().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZ::SerializeContext::ClassData* classData = dataModel->GetComponents()[sourceRow];
|
||||
if (!classData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Category
|
||||
if (!m_selectedCategory.empty())
|
||||
{
|
||||
AZStd::string currentCateogry = ComponentDataModel::GetCategory(classData);
|
||||
|
||||
if (AzFramework::StringFunc::Find(currentCateogry.c_str(), m_selectedCategory.c_str()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (filterRegExp().isValid())
|
||||
{
|
||||
QString componentName = QString::fromUtf8(classData->m_editData->m_name);
|
||||
return componentName.contains(filterRegExp());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ComponentDataProxyModel::SetSelectedCategory(const AZStd::string& category)
|
||||
{
|
||||
m_selectedCategory = category;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ComponentDataProxyModel::ClearSelectedCategory()
|
||||
{
|
||||
m_selectedCategory.clear();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
#include "UI/ComponentPalette/moc_ComponentDataModel.cpp"
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QAbstractTableModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
|
||||
#include <AzQtComponents/Buses/DragAndDrop.h>
|
||||
#endif
|
||||
|
||||
namespace ComponentDataUtilities
|
||||
{
|
||||
// Given a list of selected components, use the provided model to get the components to add to any selected entities.
|
||||
void AddComponentsToSelectedEntities(const QModelIndexList& selectedComponents, QAbstractItemModel* model);
|
||||
}
|
||||
|
||||
class CViewport;
|
||||
|
||||
//! ComponentDataModel
|
||||
//! Holds the data required to display components in a table, this includes component name, categories, icons.
|
||||
class ComponentDataModel
|
||||
: public QAbstractTableModel
|
||||
, protected AzQtComponents::DragAndDropEventsBus::Handler // its okay if more than one of these is installed, the first one gets it.
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ComponentDataModel, AZ::SystemAllocator, 0);
|
||||
|
||||
using ComponentClassList = AZStd::vector<const AZ::SerializeContext::ClassData*>;
|
||||
using ComponentCategorySet = AZStd::set<AZStd::string>;
|
||||
using ComponentClassMap = AZStd::unordered_map<AZStd::string, AZStd::vector<const AZ::SerializeContext::ClassData*>>;
|
||||
using ComponentIconMap = AZStd::unordered_map<AZ::Uuid, QIcon>;
|
||||
|
||||
enum ColumnIndex
|
||||
{
|
||||
Icon,
|
||||
Category,
|
||||
Name,
|
||||
Count
|
||||
};
|
||||
|
||||
enum CustomRoles
|
||||
{
|
||||
ClassDataRole = Qt::UserRole + 1
|
||||
};
|
||||
|
||||
ComponentDataModel(QObject* parent = nullptr);
|
||||
~ComponentDataModel() override;
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex &child) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
QMimeData* mimeData(const QModelIndexList& indexes) const override;
|
||||
|
||||
const AZ::SerializeContext::ClassData* GetClassData(const QModelIndex&) const;
|
||||
|
||||
AZ::EntityId NewEntityFromSelection(const QModelIndexList& selection);
|
||||
|
||||
static const char* GetCategory(const AZ::SerializeContext::ClassData* classData);
|
||||
|
||||
ComponentClassList& GetComponents() { return m_componentList; }
|
||||
ComponentCategorySet& GetCategories() { return m_categories; }
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AzQtComponents::DragAndDropEventsBus::Handler
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void DragEnter(QDragEnterEvent* event, AzQtComponents::DragAndDropContextBase& context) override;
|
||||
void DragMove(QDragMoveEvent* event, AzQtComponents::DragAndDropContextBase& context) override;
|
||||
void DragLeave(QDragLeaveEvent* event) override;
|
||||
void Drop(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context) override;
|
||||
bool CanAcceptDragAndDropEvent(QDropEvent* event, AzQtComponents::DragAndDropContextBase& context) const;
|
||||
|
||||
ComponentClassList m_componentList;
|
||||
ComponentClassMap m_componentMap;
|
||||
ComponentIconMap m_componentIcons;
|
||||
ComponentCategorySet m_categories;
|
||||
};
|
||||
|
||||
//! ComponentDataProxyModel
|
||||
//! FilterProxy for the ComponentDataModel is used along with the search criteria to filter the
|
||||
//! list of components based on tags and/or selected category.
|
||||
class ComponentDataProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ComponentDataProxyModel, AZ::SystemAllocator, 0);
|
||||
|
||||
ComponentDataProxyModel(QObject* parent = nullptr)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{}
|
||||
|
||||
// Creates a new entity and adds the selected components to it.
|
||||
// It is specialized here to ensure it uses the correct indices according to the sorted data.
|
||||
AZ::EntityId NewEntityFromSelection(const QModelIndexList& selection);
|
||||
|
||||
// Filters rows according to the specifed tags and/or selected category
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
|
||||
|
||||
// Set the category to filter by.
|
||||
void SetSelectedCategory(const AZStd::string& category);
|
||||
void ClearSelectedCategory();
|
||||
|
||||
protected:
|
||||
|
||||
AZStd::string m_selectedCategory;
|
||||
};
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Memory/SystemAllocator.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/UserSettings/UserSettings.h>
|
||||
|
||||
//=============================================================================
|
||||
|
||||
class ComponentPaletteSettings
|
||||
: public AZ::UserSettings
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ComponentPaletteSettings, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(ComponentPaletteSettings, "{BAC3BABA-6DF1-4EEE-AFF1-6A84AD1820A1}", AZ::UserSettings);
|
||||
|
||||
AZStd::vector<AZ::Uuid> m_favorites;
|
||||
|
||||
void SetFavorites(AZStd::vector<AZ::Uuid>&& componentIds)
|
||||
{
|
||||
m_favorites = AZStd::move(componentIds);
|
||||
}
|
||||
|
||||
void RemoveFavorites(const AZStd::vector<AZ::Uuid>& componentIds)
|
||||
{
|
||||
for (const AZ::Uuid& componentId : componentIds)
|
||||
{
|
||||
auto favoriteIterator = AZStd::find(m_favorites.begin(), m_favorites.end(), componentId);
|
||||
AZ_Assert(favoriteIterator != m_favorites.end(), "Component Palette Favorite not found.");
|
||||
|
||||
if (favoriteIterator != m_favorites.end())
|
||||
{
|
||||
m_favorites.erase(favoriteIterator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char* GetSettingsFile()
|
||||
{
|
||||
static const char* settingsFile("@user@/editor/componentpalette.usersettings");
|
||||
return settingsFile;
|
||||
}
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<ComponentPaletteSettings>()
|
||||
->Version(1)
|
||||
->Field("m_favorites", &ComponentPaletteSettings::m_favorites)
|
||||
;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ComponentEntityEditorPlugin_precompiled.h"
|
||||
|
||||
#include "ComponentPaletteWindow.h"
|
||||
#include "ComponentDataModel.h"
|
||||
#include "FavoriteComponentList.h"
|
||||
#include "FilteredComponentList.h"
|
||||
#include "CategoriesList.h"
|
||||
|
||||
#include <LyViewPaneNames.h>
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
|
||||
#include <AzToolsFramework/UI/SearchWidget/SearchCriteriaWidget.hxx>
|
||||
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/API/ViewPaneOptions.h>
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
ComponentPaletteWindow::ComponentPaletteWindow(QWidget* parent)
|
||||
: QMainWindow(parent)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
void ComponentPaletteWindow::Init()
|
||||
{
|
||||
layout()->setSizeConstraint(QLayout::SetMinimumSize);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setSizeConstraint(QLayout::SetMinimumSize);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
QHBoxLayout* gridLayout = new QHBoxLayout(NULL);
|
||||
gridLayout->setSizeConstraint(QLayout::SetMaximumSize);
|
||||
gridLayout->setContentsMargins(0, 0, 0, 0);
|
||||
gridLayout->setSpacing(0);
|
||||
|
||||
m_filterWidget = new AzToolsFramework::SearchCriteriaWidget(this);
|
||||
|
||||
QStringList tags;
|
||||
tags << tr("name");
|
||||
m_filterWidget->SetAcceptedTags(tags, tags[0]);
|
||||
layout->addLayout(gridLayout, 1);
|
||||
|
||||
// Left Panel
|
||||
QVBoxLayout* leftPaneLayout = new QVBoxLayout(this);
|
||||
|
||||
// Favorites
|
||||
leftPaneLayout->addWidget(new QLabel(tr("Favorites")));
|
||||
leftPaneLayout->addWidget(new QLabel(tr("Drag components here to add favorites.")));
|
||||
FavoritesList* favorites = new FavoritesList();
|
||||
favorites->Init();
|
||||
leftPaneLayout->addWidget(favorites);
|
||||
|
||||
// Categories
|
||||
m_categoryListWidget = new ComponentCategoryList();
|
||||
m_categoryListWidget->Init();
|
||||
leftPaneLayout->addWidget(m_categoryListWidget);
|
||||
gridLayout->addLayout(leftPaneLayout);
|
||||
|
||||
// Right Panel
|
||||
QVBoxLayout* rightPanelLayout = new QVBoxLayout(this);
|
||||
gridLayout->addLayout(rightPanelLayout);
|
||||
|
||||
// Component list
|
||||
m_componentListWidget = new FilteredComponentList(this);
|
||||
m_componentListWidget->Init();
|
||||
|
||||
rightPanelLayout->addWidget(new QLabel(tr("Components")));
|
||||
rightPanelLayout->addWidget(m_filterWidget, 0, Qt::AlignTop);
|
||||
rightPanelLayout->addWidget(m_componentListWidget);
|
||||
|
||||
// The main window
|
||||
QWidget* window = new QWidget();
|
||||
window->setLayout(layout);
|
||||
setCentralWidget(window);
|
||||
|
||||
connect(m_categoryListWidget, &ComponentCategoryList::OnCategoryChange, m_componentListWidget, &FilteredComponentList::SetCategory);
|
||||
connect(m_filterWidget, &AzToolsFramework::SearchCriteriaWidget::SearchCriteriaChanged, m_componentListWidget, &FilteredComponentList::SearchCriteriaChanged);
|
||||
|
||||
}
|
||||
|
||||
void ComponentPaletteWindow::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (event->modifiers().testFlag(Qt::ControlModifier) && event->key() == Qt::Key_F)
|
||||
{
|
||||
m_filterWidget->SelectTextEntryBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMainWindow::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentPaletteWindow::RegisterViewClass()
|
||||
{
|
||||
using namespace AzToolsFramework;
|
||||
|
||||
ViewPaneOptions options;
|
||||
options.canHaveMultipleInstances = true;
|
||||
RegisterViewPane<ComponentPaletteWindow>("Component Palette", LyViewPane::CategoryOther, options);
|
||||
}
|
||||
|
||||
#include <UI/ComponentPalette/moc_ComponentPaletteWindow.cpp>
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QMainWindow>
|
||||
#endif
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
class SearchCriteriaWidget;
|
||||
}
|
||||
|
||||
class ComponentCategoryList;
|
||||
class FilteredComponentList;
|
||||
class ComponentDataModel;
|
||||
|
||||
//! ComponentPaletteWindow
|
||||
//! Provides a window with controls related to the Component Entity system. It provides an intuitive and organized
|
||||
//! set of controls to display, sort, filter components. It provides mechanisms for creating entities by dragging
|
||||
//! and dropping components into the viewport as well as from context menus.
|
||||
class ComponentPaletteWindow
|
||||
: public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
explicit ComponentPaletteWindow(QWidget* parent = 0);
|
||||
|
||||
void Init();
|
||||
|
||||
static const GUID& GetClassID()
|
||||
{
|
||||
// {4236998F-1138-466D-9DF5-6533BFA1DFCA}
|
||||
static const GUID guid =
|
||||
{
|
||||
0x4236998F, 0x1138, 0x466D, { 0x9D, 0xF5, 0x65, 0x33, 0xBF, 0xA1, 0xDF, 0xCA }
|
||||
};
|
||||
return guid;
|
||||
}
|
||||
|
||||
static void RegisterViewClass();
|
||||
|
||||
protected:
|
||||
ComponentCategoryList* m_categoryListWidget;
|
||||
FilteredComponentList* m_componentListWidget;
|
||||
AzToolsFramework::SearchCriteriaWidget* m_filterWidget;
|
||||
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
};
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ComponentEntityEditorPlugin_precompiled.h"
|
||||
|
||||
#include "FavoriteComponentList.h"
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
|
||||
#include <Editor/CryEditDoc.h>
|
||||
#include <Editor/ViewManager.h>
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QMimeData>
|
||||
|
||||
// FavoritesList
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FavoritesList::FavoritesList(QWidget* parent /*= nullptr*/)
|
||||
: FilteredComponentList(parent)
|
||||
{
|
||||
}
|
||||
|
||||
FavoritesList::~FavoritesList()
|
||||
{
|
||||
FavoriteComponentListRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void FavoritesList::Init()
|
||||
{
|
||||
FavoriteComponentListRequestBus::Handler::BusConnect();
|
||||
|
||||
FavoritesDataModel* favoritesDataModel = new FavoritesDataModel(this);
|
||||
|
||||
setModel(favoritesDataModel);
|
||||
|
||||
horizontalHeader()->setSectionResizeMode(ComponentDataModel::ColumnIndex::Name, QHeaderView::Stretch);
|
||||
|
||||
setShowGrid(false);
|
||||
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
|
||||
setStyleSheet("QTableView { selection-background-color: rgba(255,255,255,0.2); }");
|
||||
setGridStyle(Qt::PenStyle::NoPen);
|
||||
verticalHeader()->hide();
|
||||
horizontalHeader()->hide();
|
||||
setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
|
||||
setShowGrid(false);
|
||||
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
|
||||
setColumnWidth(ComponentDataModel::ColumnIndex::Icon, 32);
|
||||
hideColumn(ComponentDataModel::ColumnIndex::Category);
|
||||
|
||||
setDragDropMode(QAbstractItemView::DragDrop);
|
||||
setAcceptDrops(true);
|
||||
|
||||
horizontalHeader()->setSectionResizeMode(ComponentDataModel::ColumnIndex::Icon, QHeaderView::ResizeToContents);
|
||||
setColumnWidth(ComponentDataModel::ColumnIndex::Icon, 32);
|
||||
|
||||
horizontalHeader()->setSectionResizeMode(ComponentDataModel::ColumnIndex::Category, QHeaderView::Stretch);
|
||||
setColumnWidth(ComponentDataModel::ColumnIndex::Category, 90);
|
||||
|
||||
// Context menu
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &QWidget::customContextMenuRequested, this, &FavoritesList::ShowContextMenu);
|
||||
}
|
||||
|
||||
void FavoritesList::ShowContextMenu(const QPoint& pos)
|
||||
{
|
||||
// Only show if a level is loaded
|
||||
if (!GetIEditor() || GetIEditor()->IsInGameMode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( model()->rowCount() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu contextMenu(tr("Context menu"), this);
|
||||
|
||||
QAction actionNewEntity(tr("Make entity with selected favorites"), this);
|
||||
QAction actionAddToSelection(this);
|
||||
if (GetIEditor()->GetDocument()->IsDocumentReady())
|
||||
{
|
||||
QObject::connect(&actionNewEntity, &QAction::triggered, this, [&] { ContextMenu_NewEntity(); });
|
||||
contextMenu.addAction(&actionNewEntity);
|
||||
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
EBUS_EVENT_RESULT(selectedEntities, AzToolsFramework::ToolsApplicationRequests::Bus, GetSelectedEntities);
|
||||
|
||||
if (!selectedEntities.empty())
|
||||
{
|
||||
QString addToSelection = selectedEntities.size() > 1 ? tr("Add to selected entities") : tr("Add to selected entity");
|
||||
actionAddToSelection.setText(addToSelection);
|
||||
|
||||
QObject::connect(&actionAddToSelection, &QAction::triggered, this, [&] { ContextMenu_AddToSelectedEntities(); });
|
||||
contextMenu.addAction(&actionAddToSelection);
|
||||
}
|
||||
|
||||
contextMenu.addSeparator();
|
||||
}
|
||||
|
||||
QAction action(tr("Remove"), this);
|
||||
QObject::connect(&action, &QAction::triggered, this, [&] { ContextMenu_RemoveSelectedFavorites(); });
|
||||
contextMenu.addAction(&action);
|
||||
|
||||
contextMenu.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void FavoritesList::ContextMenu_RemoveSelectedFavorites()
|
||||
{
|
||||
FavoritesDataModel* dataModel = qobject_cast<FavoritesDataModel*>(model());
|
||||
if (!selectedIndexes().empty())
|
||||
{
|
||||
dataModel->Remove(selectedIndexes());
|
||||
}
|
||||
}
|
||||
|
||||
void FavoritesList::rowsInserted([[maybe_unused]] const QModelIndex& parent, [[maybe_unused]] int start, [[maybe_unused]] int end)
|
||||
{
|
||||
resizeRowToContents(0);
|
||||
}
|
||||
|
||||
void FavoritesList::AddFavorites(const AZStd::vector<const AZ::SerializeContext::ClassData*>& classDataContainer)
|
||||
{
|
||||
for (const AZ::SerializeContext::ClassData* classData : classDataContainer)
|
||||
{
|
||||
if (classData)
|
||||
{
|
||||
FavoritesDataModel* dataModel = qobject_cast<FavoritesDataModel*>(model());
|
||||
dataModel->AddFavorite(classData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FavoritesList::dragEnterEvent(QDragEnterEvent* event)
|
||||
{
|
||||
if (event->mimeData()->hasFormat(AzToolsFramework::ComponentTypeMimeData::GetMimeType()))
|
||||
{
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void FavoritesList::dragMoveEvent(QDragMoveEvent* event)
|
||||
{
|
||||
if (event->source() == this)
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
else
|
||||
{
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
||||
// FavoritesDataModel
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int FavoritesDataModel::rowCount([[maybe_unused]] const QModelIndex &parent /*= QModelIndex()*/) const
|
||||
{
|
||||
return m_favorites.size();
|
||||
}
|
||||
|
||||
int FavoritesDataModel::columnCount([[maybe_unused]] const QModelIndex &parent /*= QModelIndex()*/) const
|
||||
{
|
||||
return ColumnIndex::Count;
|
||||
}
|
||||
|
||||
void FavoritesDataModel::SaveState()
|
||||
{
|
||||
AZStd::vector<AZ::Uuid> favorites;
|
||||
for (const AZ::SerializeContext::ClassData* classData : m_favorites)
|
||||
{
|
||||
favorites.push_back(classData->m_typeId);
|
||||
}
|
||||
m_settings->SetFavorites(AZStd::move(favorites));
|
||||
|
||||
|
||||
// Write the settings to file...
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(serializeContext, "Serialize Context is null!");
|
||||
|
||||
char settingsPath[AZ_MAX_PATH_LEN] = { 0 };
|
||||
AZ::IO::FileIOBase::GetInstance()->ResolvePath(ComponentPaletteSettings::GetSettingsFile(), settingsPath, AZ_MAX_PATH_LEN);
|
||||
|
||||
bool result = m_provider.Save(settingsPath, serializeContext);
|
||||
(void)result;
|
||||
AZ_Warning("ComponentPaletteSettings", result, "Failed to Save the Component Palette Settings!");
|
||||
}
|
||||
|
||||
void FavoritesDataModel::LoadState()
|
||||
{
|
||||
// It is necessary to Load the settings file *before* you call UserSettings::CreateFind!
|
||||
AZ::SerializeContext* serializeContext = nullptr;
|
||||
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
|
||||
AZ_Assert(serializeContext, "Serialize Context is null!");
|
||||
|
||||
char settingsPath[AZ_MAX_PATH_LEN] = { 0 };
|
||||
AZ::IO::FileIOBase::GetInstance()->ResolvePath(ComponentPaletteSettings::GetSettingsFile(), settingsPath, AZ_MAX_PATH_LEN);
|
||||
|
||||
bool result = m_provider.Load(settingsPath, serializeContext);
|
||||
(void)result;
|
||||
|
||||
|
||||
// Create (if no file was found) or find the settings, this will populate the m_settings->m_favorites list.
|
||||
m_settings = AZ::UserSettings::CreateFind<ComponentPaletteSettings>(AZ_CRC("ComponentPaletteSettings", 0x481d355b), m_providerId);
|
||||
|
||||
// Add favorites to the data model from loaded settings
|
||||
for (const AZ::Uuid& favorite : m_settings->m_favorites)
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(favorite);
|
||||
if (classData)
|
||||
{
|
||||
AddFavorite(classData, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FavoritesDataModel::Remove(const QModelIndexList& indices)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
auto newFavorites = m_favorites;
|
||||
|
||||
// swap here
|
||||
for (auto index : indices)
|
||||
{
|
||||
// we're only dealing with columns and they're the only thing with class data anyways
|
||||
if (index.column() == 0)
|
||||
{
|
||||
QVariant classDataVariant = index.data(ComponentDataModel::ClassDataRole);
|
||||
if (classDataVariant.isValid())
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* classData = reinterpret_cast<const AZ::SerializeContext::ClassData*>(classDataVariant.value<void*>());
|
||||
newFavorites.removeAll(classData);
|
||||
|
||||
AZ_TracePrintf("Debug", "Removing: %s\n", classData->m_editData->m_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_favorites.swap(newFavorites);
|
||||
|
||||
endResetModel();
|
||||
|
||||
SaveState();
|
||||
}
|
||||
|
||||
QModelIndex FavoritesDataModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent))
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
if (row >= rowCount(parent) || column >= columnCount(parent))
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
return createIndex(row, column, (void*)(m_favorites[row]));
|
||||
}
|
||||
|
||||
QVariant FavoritesDataModel::data(const QModelIndex &index, int role /*= Qt::DisplayRole*/) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const AZ::SerializeContext::ClassData* classData = m_favorites[index.row()];
|
||||
if (!classData)
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
if (index.column() == ComponentDataModel::ColumnIndex::Name)
|
||||
{
|
||||
if (m_favorites.empty())
|
||||
{
|
||||
return QVariant(tr("You have 0 favorites.\nDrag some components here."));
|
||||
}
|
||||
|
||||
return QVariant(classData->m_editData->m_name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::DecorationRole:
|
||||
{
|
||||
if (index.column() == ColumnIndex::Icon)
|
||||
{
|
||||
const AZ::SerializeContext::ClassData* iconClassData = m_favorites[index.row()];
|
||||
auto iconIterator = m_componentIcons.find(iconClassData->m_typeId);
|
||||
if (iconIterator != m_componentIcons.end())
|
||||
{
|
||||
return iconIterator->second;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ClassDataRole:
|
||||
if (index.column() == 0) // Only get data for one column
|
||||
{
|
||||
return QVariant::fromValue<void*>(reinterpret_cast<void*>(const_cast<AZ::SerializeContext::ClassData*>(classData)));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ComponentDataModel::data(index, role);
|
||||
|
||||
}
|
||||
|
||||
void FavoritesDataModel::SetSavedStateKey([[maybe_unused]] AZ::u32 key)
|
||||
{
|
||||
}
|
||||
|
||||
FavoritesDataModel::FavoritesDataModel(QWidget* parent /*= nullptr*/)
|
||||
: ComponentDataModel(parent)
|
||||
, m_providerId(AZ_CRC("ComponentPaletteSettingsProviderId"))
|
||||
{
|
||||
m_provider.Activate(m_providerId);
|
||||
LoadState();
|
||||
}
|
||||
|
||||
FavoritesDataModel::~FavoritesDataModel()
|
||||
{
|
||||
m_provider.Deactivate();
|
||||
}
|
||||
|
||||
void FavoritesDataModel::AddFavorite(const AZ::SerializeContext::ClassData* classData, bool updateSettings)
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
if (m_favorites.indexOf(classData) < 0)
|
||||
{
|
||||
m_favorites.push_back(classData);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
|
||||
if (updateSettings)
|
||||
{
|
||||
SaveState();
|
||||
}
|
||||
}
|
||||
|
||||
bool FavoritesDataModel::dropMimeData(const QMimeData *data, Qt::DropAction action, [[maybe_unused]] int row, [[maybe_unused]] int column, [[maybe_unused]] const QModelIndex &parent)
|
||||
{
|
||||
if (action == Qt::IgnoreAction)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data && data->hasFormat(AzToolsFramework::ComponentTypeMimeData::GetMimeType()))
|
||||
{
|
||||
AzToolsFramework::ComponentTypeMimeData::ClassDataContainer classDataContainer;
|
||||
AzToolsFramework::ComponentTypeMimeData::Get(data, classDataContainer);
|
||||
|
||||
for (const AZ::SerializeContext::ClassData* classData : classDataContainer)
|
||||
{
|
||||
if (classData)
|
||||
{
|
||||
AddFavorite(classData);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#include <UI/ComponentPalette/moc_FavoriteComponentList.cpp>
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include "ComponentDataModel.h"
|
||||
#include "FilteredComponentList.h"
|
||||
#include "ComponentPaletteSettings.h"
|
||||
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/UserSettings/UserSettingsProvider.h>
|
||||
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
|
||||
#include <AzToolsFramework/ToolsComponents/ComponentAssetMimeDataContainer.h>
|
||||
#endif
|
||||
|
||||
//! FavoriteComponentListRequest
|
||||
//! Bus that provides a way for external features to record favorites
|
||||
class FavoriteComponentListRequest : public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
virtual void AddFavorites(const AZStd::vector<const AZ::SerializeContext::ClassData*>&) = 0;
|
||||
};
|
||||
|
||||
using FavoriteComponentListRequestBus = AZ::EBus<FavoriteComponentListRequest>;
|
||||
|
||||
|
||||
//! FavoritesDataModel
|
||||
//! Stores the list of component class data to display in the favorites control, offers persistence through user settings.
|
||||
class FavoritesDataModel
|
||||
: public ComponentDataModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
AZ_CLASS_ALLOCATOR(FavoritesDataModel, AZ::SystemAllocator, 0);
|
||||
|
||||
FavoritesDataModel(QWidget* parent = nullptr);
|
||||
~FavoritesDataModel() override;
|
||||
|
||||
//! Add a favorite component
|
||||
//! \param classData The ClassData information for the component to store as favorite
|
||||
//! \param updateSettings Optional parameter used to determine if the persistent settings need to be updated.
|
||||
void AddFavorite(const AZ::SerializeContext::ClassData* classData, bool updateSettings = true);
|
||||
|
||||
//! Remove all the specified items from the table
|
||||
//! \param indices List of indices to remove from favorites
|
||||
void Remove(const QModelIndexList& indices);
|
||||
|
||||
//! Save the list of favorite components to user settings
|
||||
void SaveState();
|
||||
|
||||
//! Load the list of favorite components from user settings
|
||||
void LoadState();
|
||||
|
||||
protected:
|
||||
|
||||
void SetSavedStateKey(AZ::u32 key);
|
||||
|
||||
// Qt handlers
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
|
||||
|
||||
// List of component class data
|
||||
QList<const AZ::SerializeContext::ClassData*> m_favorites;
|
||||
|
||||
// The Palette settings and provider information for saving out the Favorites list
|
||||
AZStd::intrusive_ptr<ComponentPaletteSettings> m_settings;
|
||||
AZ::UserSettingsProvider m_provider;
|
||||
AZ::u32 m_providerId;
|
||||
};
|
||||
|
||||
|
||||
//! FavoritesList
|
||||
//! User customized list of favorite components, provides persistence.
|
||||
class FavoritesList
|
||||
: public FilteredComponentList
|
||||
, FavoriteComponentListRequestBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
explicit FavoritesList(QWidget* parent = nullptr);
|
||||
~FavoritesList() override;
|
||||
|
||||
void Init() override;
|
||||
|
||||
protected:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FavoriteComponentListRequestBus
|
||||
void AddFavorites(const AZStd::vector<const AZ::SerializeContext::ClassData*>& classDataContainer) override;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void rowsInserted(const QModelIndex& parent, int start, int end);
|
||||
|
||||
// Context menu handlers
|
||||
void ShowContextMenu(const QPoint&);
|
||||
void ContextMenu_RemoveSelectedFavorites();
|
||||
|
||||
// Validate data being dragged in
|
||||
void dragEnterEvent(QDragEnterEvent * event) override;
|
||||
void dragMoveEvent(QDragMoveEvent* event) override;
|
||||
|
||||
//! Handler used when dropping PaletteItems into the Viewport.
|
||||
static void DragDropHandler(CViewport* viewport, int ptx, int pty, void* custom);
|
||||
};
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ComponentEntityEditorPlugin_precompiled.h"
|
||||
|
||||
#include "ComponentDataModel.h"
|
||||
#include "FavoriteComponentList.h"
|
||||
#include "FilteredComponentList.h"
|
||||
|
||||
#include "CryCommon/MathConversion.h"
|
||||
#include "Editor/IEditor.h"
|
||||
#include "Editor/ViewManager.h"
|
||||
#include <Editor/CryEditDoc.h>
|
||||
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
|
||||
#include <QHeaderView>
|
||||
|
||||
void FilteredComponentList::Init()
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
setDragDropMode(QAbstractItemView::DragDropMode::DragOnly);
|
||||
setDragEnabled(true);
|
||||
|
||||
setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
|
||||
setStyleSheet("QTreeWidget { selection-background-color: rgba(255,255,255,0.2); }");
|
||||
setGridStyle(Qt::PenStyle::NoPen);
|
||||
verticalHeader()->hide();
|
||||
horizontalHeader()->hide();
|
||||
setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
|
||||
setAcceptDrops(false);
|
||||
|
||||
m_componentDataModel = new ComponentDataModel(this);
|
||||
ComponentDataProxyModel* componentDataProxyModel = new ComponentDataProxyModel(this);
|
||||
componentDataProxyModel->setSourceModel(m_componentDataModel);
|
||||
setModel(componentDataProxyModel);
|
||||
|
||||
QHeaderView* horizontalHeaderView = horizontalHeader();
|
||||
horizontalHeaderView->setSectionResizeMode(ComponentDataModel::ColumnIndex::Icon, QHeaderView::ResizeToContents);
|
||||
horizontalHeaderView->setSectionResizeMode(ComponentDataModel::ColumnIndex::Name, QHeaderView::Stretch);
|
||||
|
||||
setColumnWidth(ComponentDataModel::ColumnIndex::Icon, 32);
|
||||
setShowGrid(false);
|
||||
|
||||
setColumnWidth(ComponentDataModel::ColumnIndex::Name, 90);
|
||||
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
|
||||
sortByColumn(ComponentDataModel::ColumnIndex::Name, Qt::AscendingOrder);
|
||||
hideColumn(ComponentDataModel::ColumnIndex::Category);
|
||||
|
||||
connect(model(), &QAbstractItemModel::rowsInserted, this, &FilteredComponentList::rowsInserted);
|
||||
connect(model(), &QAbstractItemModel::rowsRemoved, this, &FilteredComponentList::rowsAboutToBeRemoved);
|
||||
|
||||
connect(model(), SIGNAL(modelReset()), SLOT(modelReset()));
|
||||
|
||||
|
||||
// Context menu
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(this, &QWidget::customContextMenuRequested, this, &FilteredComponentList::ShowContextMenu);
|
||||
}
|
||||
|
||||
void FilteredComponentList::ContextMenu_NewEntity()
|
||||
{
|
||||
AZ::EntityId entityId;
|
||||
|
||||
auto proxyDataModel = qobject_cast<ComponentDataProxyModel*>(model());
|
||||
if (proxyDataModel)
|
||||
{
|
||||
entityId = proxyDataModel->NewEntityFromSelection(selectedIndexes());
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dataModel = qobject_cast<ComponentDataModel*>(model());
|
||||
if (dataModel)
|
||||
{
|
||||
entityId = dataModel->NewEntityFromSelection(selectedIndexes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FilteredComponentList::ContextMenu_AddToFavorites()
|
||||
{
|
||||
AZStd::vector<const AZ::SerializeContext::ClassData*> componentsToAdd;
|
||||
for (auto index : selectedIndexes())
|
||||
{
|
||||
QVariant classDataVariant = index.data(ComponentDataModel::ClassDataRole);
|
||||
if (classDataVariant.isValid())
|
||||
{
|
||||
auto classData = reinterpret_cast<const AZ::SerializeContext::ClassData*>(classDataVariant.value<void*>());
|
||||
componentsToAdd.push_back(classData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!componentsToAdd.empty())
|
||||
{
|
||||
EBUS_EVENT(FavoriteComponentListRequestBus, AddFavorites, componentsToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
void FilteredComponentList::ContextMenu_AddToSelectedEntities()
|
||||
{
|
||||
ComponentDataUtilities::AddComponentsToSelectedEntities(selectedIndexes(), model());
|
||||
}
|
||||
|
||||
void FilteredComponentList::ShowContextMenu(const QPoint& pos)
|
||||
{
|
||||
QMenu contextMenu(tr("Context menu"), this);
|
||||
|
||||
QAction actionNewEntity(tr("Create new entity with selected components"), this);
|
||||
if (GetIEditor()->GetDocument()->IsDocumentReady())
|
||||
{
|
||||
QObject::connect(&actionNewEntity, &QAction::triggered, this, [this] { ContextMenu_NewEntity(); });
|
||||
contextMenu.addAction(&actionNewEntity);
|
||||
}
|
||||
|
||||
QAction actionAddFavorite(tr("Add to favorites"), this);
|
||||
QObject::connect(&actionAddFavorite, &QAction::triggered, this, [this] { ContextMenu_AddToFavorites(); });
|
||||
contextMenu.addAction(&actionAddFavorite);
|
||||
|
||||
QAction actionAddToSelection(this);
|
||||
if (GetIEditor()->GetDocument()->IsDocumentReady())
|
||||
{
|
||||
AzToolsFramework::EntityIdList selectedEntities;
|
||||
EBUS_EVENT_RESULT(selectedEntities, AzToolsFramework::ToolsApplicationRequests::Bus, GetSelectedEntities);
|
||||
|
||||
if (!selectedEntities.empty())
|
||||
{
|
||||
QString addToSelection = selectedEntities.size() > 1 ? tr("Add to selected entities") : tr("Add to selected entity");
|
||||
|
||||
actionAddToSelection.setText(addToSelection);
|
||||
QObject::connect(&actionAddToSelection, &QAction::triggered, this, [this] { ContextMenu_AddToSelectedEntities(); });
|
||||
contextMenu.addAction(&actionAddToSelection);
|
||||
}
|
||||
}
|
||||
// TODO: Requires information panel implementation LMBR-28174
|
||||
//QAction actionHelp(tr("Help"), this);
|
||||
//QObject::connect(&actionHelp, &QAction::triggered, this, [&] {});
|
||||
//contextMenu.addAction(&actionHelp);
|
||||
|
||||
contextMenu.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void FilteredComponentList::modelReset()
|
||||
{
|
||||
// Ensure that the category column is hidden
|
||||
hideColumn(ComponentDataModel::ColumnIndex::Category);
|
||||
}
|
||||
|
||||
FilteredComponentList::FilteredComponentList(QWidget* parent /*= nullptr*/)
|
||||
: QTableView(parent)
|
||||
{
|
||||
}
|
||||
|
||||
FilteredComponentList::~FilteredComponentList()
|
||||
{
|
||||
}
|
||||
|
||||
void FilteredComponentList::SearchCriteriaChanged(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator)
|
||||
{
|
||||
setUpdatesEnabled(false);
|
||||
|
||||
auto dataModel = qobject_cast<ComponentDataProxyModel*>(model());
|
||||
if (dataModel)
|
||||
{
|
||||
// Go through the list of items and show/hide as needed due to filter.
|
||||
QString filter;
|
||||
for (const auto& criteria : criteriaList)
|
||||
{
|
||||
QString tag, text;
|
||||
AzToolsFramework::SearchCriteriaButton::SplitTagAndText(criteria, tag, text);
|
||||
AppendFilter(filter, text, filterOperator);
|
||||
}
|
||||
|
||||
dataModel->setFilterRegExp(QRegExp(filter, Qt::CaseSensitivity::CaseInsensitive));
|
||||
}
|
||||
|
||||
setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
void FilteredComponentList::SetCategory(const char* category)
|
||||
{
|
||||
auto dataModel = qobject_cast<ComponentDataProxyModel*>(model());
|
||||
if (dataModel)
|
||||
{
|
||||
if (!category || category[0] == 0 || azstricmp(category, "All") == 0)
|
||||
{
|
||||
dataModel->ClearSelectedCategory();
|
||||
}
|
||||
else
|
||||
{
|
||||
dataModel->SetSelectedCategory(category);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this ensures the category column remains hidden
|
||||
hideColumn(ComponentDataModel::ColumnIndex::Category);
|
||||
}
|
||||
|
||||
void FilteredComponentList::BuildFilter(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator)
|
||||
{
|
||||
ClearFilterRegExp();
|
||||
|
||||
for (const auto& criteria : criteriaList)
|
||||
{
|
||||
QString tag, text;
|
||||
AzToolsFramework::SearchCriteriaButton::SplitTagAndText(criteria, tag, text);
|
||||
if (tag.isEmpty())
|
||||
{
|
||||
tag = "null";
|
||||
}
|
||||
|
||||
QString filter = m_filtersRegExp[tag.toStdString().c_str()].pattern();
|
||||
|
||||
AppendFilter(filter, text, filterOperator);
|
||||
|
||||
SetFilterRegExp(tag.toStdString().c_str(), QRegExp(filter, Qt::CaseInsensitive));
|
||||
}
|
||||
}
|
||||
|
||||
void FilteredComponentList::AppendFilter(QString& filter, const QString& text, AzToolsFramework::FilterOperatorType filterOperator)
|
||||
{
|
||||
if (filterOperator == AzToolsFramework::FilterOperatorType::Or)
|
||||
{
|
||||
if (filter.isEmpty())
|
||||
{
|
||||
filter = text;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter += "|" + text;
|
||||
}
|
||||
}
|
||||
else if (filterOperator == AzToolsFramework::FilterOperatorType::And)
|
||||
{
|
||||
//using lookaheads to produce an "and" effect.
|
||||
filter += "(?=.*" + text + ")";
|
||||
}
|
||||
}
|
||||
|
||||
void FilteredComponentList::SetFilterRegExp(const AZStd::string& filterType, const QRegExp& regExp)
|
||||
{
|
||||
m_filtersRegExp[filterType] = regExp;
|
||||
}
|
||||
|
||||
void FilteredComponentList::ClearFilterRegExp(const AZStd::string& filterType /*= AZStd::string()*/)
|
||||
{
|
||||
if (filterType.empty())
|
||||
{
|
||||
for (auto& it : m_filtersRegExp)
|
||||
{
|
||||
it.second = QRegExp();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_filtersRegExp[filterType] = QRegExp();
|
||||
}
|
||||
}
|
||||
|
||||
#include <UI/ComponentPalette/moc_FilteredComponentList.cpp>
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <QTableView>
|
||||
#include <QWidget>
|
||||
|
||||
#include <AzToolsFramework/UI/SearchWidget/SearchCriteriaWidget.hxx>
|
||||
#include <AzToolsFramework/UI/SearchWidget/SearchWidgetTypes.hxx>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include "ComponentDataModel.h"
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class SerializeContext;
|
||||
class ClassData;
|
||||
}
|
||||
|
||||
class ComponentDataModel;
|
||||
|
||||
//! FilteredComponentList
|
||||
//! Provides a list of components that can be filtered according to search criteria provided and/or from
|
||||
//! a category selection control.
|
||||
class FilteredComponentList
|
||||
: public QTableView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
explicit FilteredComponentList(QWidget* parent = nullptr);
|
||||
|
||||
~FilteredComponentList() override;
|
||||
|
||||
virtual void Init();
|
||||
|
||||
void SearchCriteriaChanged(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator);
|
||||
|
||||
void SetCategory(const char* category);
|
||||
|
||||
protected:
|
||||
|
||||
// Filtering support
|
||||
void BuildFilter(QStringList& criteriaList, AzToolsFramework::FilterOperatorType filterOperator);
|
||||
void AppendFilter(QString& filter, const QString& text, AzToolsFramework::FilterOperatorType filterOperator);
|
||||
void SetFilterRegExp(const AZStd::string& filterType, const QRegExp& regExp);
|
||||
void ClearFilterRegExp(const AZStd::string& filterType = AZStd::string());
|
||||
|
||||
// Context menu handlers
|
||||
void ShowContextMenu(const QPoint&);
|
||||
void ContextMenu_NewEntity();
|
||||
void ContextMenu_AddToFavorites();
|
||||
void ContextMenu_AddToSelectedEntities();
|
||||
|
||||
void modelReset();
|
||||
|
||||
AzToolsFramework::FilterByCategoryMap m_filtersRegExp;
|
||||
ComponentDataModel* m_componentDataModel;
|
||||
|
||||
};
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ComponentEntityEditorPlugin_precompiled.h"
|
||||
|
||||
#include "InformationPanel.h"
|
||||
|
||||
// TODO: LMBR-28174
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// TODO: LMBR-28174
|
||||
Reference in New Issue
Block a user