You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1270 lines
41 KiB
C++
1270 lines
41 KiB
C++
/*
|
|
* 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 "EditorCommon.h"
|
|
#include "AssetDropHelpers.h"
|
|
#include "QtHelpers.h"
|
|
#include <AzCore/Asset/AssetManager.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorEntityIdContainer.h>
|
|
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponentBus.h>
|
|
|
|
#include <QApplication>
|
|
#include <QMessageBox>
|
|
#include <QMouseEvent>
|
|
#include <QMimeData>
|
|
#include <QDropEvent>
|
|
#include <QDragEnterEvent>
|
|
|
|
HierarchyWidget::HierarchyWidget(EditorWindow* editorWindow)
|
|
: AzQtComponents::StyledTreeWidget()
|
|
, m_isDeleting(false)
|
|
, m_editorWindow(editorWindow)
|
|
, m_entityItemMap()
|
|
, m_itemBeingHovered(nullptr)
|
|
, m_inDragStartState(false)
|
|
, m_selectionChangedBeforeDrag(false)
|
|
, m_signalSelectionChange(true)
|
|
, m_inObjectPickMode(false)
|
|
, m_isInited(false)
|
|
{
|
|
setMouseTracking(true);
|
|
|
|
// Style.
|
|
{
|
|
setAcceptDrops(true);
|
|
setDropIndicatorShown(true);
|
|
setDragEnabled(true);
|
|
setDragDropMode(QAbstractItemView::DragDrop);
|
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
|
|
setColumnCount(kHierarchyColumnCount);
|
|
setHeader(new HierarchyHeader(this));
|
|
|
|
// IMPORTANT: This MUST be done here.
|
|
// This CAN'T be done inside HierarchyHeader.
|
|
header()->setSectionsClickable(true);
|
|
|
|
header()->setSectionResizeMode(kHierarchyColumnName, QHeaderView::Stretch);
|
|
header()->setSectionResizeMode(kHierarchyColumnIsVisible, QHeaderView::Fixed);
|
|
header()->setSectionResizeMode(kHierarchyColumnIsSelectable, QHeaderView::Fixed);
|
|
|
|
// This controls the width of the last 2 columns; both in the header and in the body of the HierarchyWidget.
|
|
header()->resizeSection(kHierarchyColumnIsVisible, UICANVASEDITOR_HIERARCHY_HEADER_ICON_SIZE);
|
|
header()->resizeSection(kHierarchyColumnIsSelectable, UICANVASEDITOR_HIERARCHY_HEADER_ICON_SIZE);
|
|
}
|
|
|
|
// Connect signals.
|
|
{
|
|
// Selection change notification.
|
|
QObject::connect(selectionModel(),
|
|
SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
|
SLOT(CurrentSelectionHasChanged(const QItemSelection &, const QItemSelection &)));
|
|
|
|
QObject::connect(model(),
|
|
SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &)),
|
|
SLOT(DataHasChanged(const QModelIndex &, const QModelIndex &, const QVector<int> &)));
|
|
}
|
|
|
|
QObject::connect(this,
|
|
&QTreeWidget::itemClicked,
|
|
this,
|
|
[this](QTreeWidgetItem* item, int column)
|
|
{
|
|
HierarchyItem* i = HierarchyItem::RttiCast(item);
|
|
|
|
if (column == kHierarchyColumnIsVisible)
|
|
{
|
|
ToggleVisibility(i);
|
|
}
|
|
else if (column == kHierarchyColumnIsSelectable)
|
|
{
|
|
CommandHierarchyItemToggleIsSelectable::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
HierarchyItemRawPtrList({i}));
|
|
}
|
|
else if (m_inObjectPickMode)
|
|
{
|
|
PickItem(i);
|
|
}
|
|
});
|
|
|
|
QObject::connect(this,
|
|
&QTreeWidget::itemExpanded,
|
|
this,
|
|
[this](QTreeWidgetItem* item)
|
|
{
|
|
CommandHierarchyItemToggleIsExpanded::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
HierarchyItem::RttiCast(item));
|
|
});
|
|
|
|
QObject::connect(this,
|
|
&QTreeWidget::itemCollapsed,
|
|
this,
|
|
[this](QTreeWidgetItem* item)
|
|
{
|
|
CommandHierarchyItemToggleIsExpanded::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
HierarchyItem::RttiCast(item));
|
|
});
|
|
|
|
EntityHighlightMessages::Bus::Handler::BusConnect();
|
|
}
|
|
|
|
HierarchyWidget::~HierarchyWidget()
|
|
{
|
|
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
|
|
EntityHighlightMessages::Bus::Handler::BusDisconnect();
|
|
}
|
|
|
|
void HierarchyWidget::SetIsDeleting(bool b)
|
|
{
|
|
m_isDeleting = b;
|
|
}
|
|
|
|
EntityHelpers::EntityToHierarchyItemMap& HierarchyWidget::GetEntityItemMap()
|
|
{
|
|
return m_entityItemMap;
|
|
}
|
|
|
|
EditorWindow* HierarchyWidget::GetEditorWindow()
|
|
{
|
|
return m_editorWindow;
|
|
}
|
|
|
|
void HierarchyWidget::ActiveCanvasChanged()
|
|
{
|
|
EntityContextChanged();
|
|
}
|
|
|
|
void HierarchyWidget::EntityContextChanged()
|
|
{
|
|
if (m_inObjectPickMode)
|
|
{
|
|
OnEntityPickModeStopped();
|
|
}
|
|
|
|
// Disconnect from the PickModeRequests bus and reconnect with the new entity context
|
|
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusDisconnect();
|
|
UiEditorEntityContext* context = m_editorWindow->GetEntityContext();
|
|
if (context)
|
|
{
|
|
AzToolsFramework::EditorPickModeNotificationBus::Handler::BusConnect(context->GetContextId());
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::CreateItems(const LyShine::EntityArray& elements)
|
|
{
|
|
std::list<AZ::Entity*> elementList(elements.begin(), elements.end());
|
|
|
|
// Build the rest of the list.
|
|
// Note: This is a breadth-first traversal through all child elements.
|
|
for (auto& e : elementList)
|
|
{
|
|
LyShine::EntityArray childElements;
|
|
EBUS_EVENT_ID_RESULT(childElements, e->GetId(), UiElementBus, GetChildElements);
|
|
elementList.insert(elementList.end(), childElements.begin(), childElements.end());
|
|
}
|
|
|
|
// Create the items.
|
|
for (auto& e : elementList)
|
|
{
|
|
AZ::Entity* parentElement = EntityHelpers::GetParentElement(e);
|
|
QTreeWidgetItem* parent = HierarchyHelpers::ElementToItem(this, parentElement, true);
|
|
AZ_Assert(parent, "No parent widget item found for parent entity");
|
|
|
|
int childIndex = -1;
|
|
EBUS_EVENT_ID_RESULT(childIndex, parentElement->GetId(), UiElementBus, GetIndexOfChild, e);
|
|
|
|
new HierarchyItem(m_editorWindow,
|
|
*parent,
|
|
childIndex,
|
|
e->GetName().c_str(),
|
|
e);
|
|
}
|
|
|
|
// restore the expanded state of all items
|
|
ApplyElementIsExpanded();
|
|
|
|
m_isInited = true;
|
|
}
|
|
|
|
void HierarchyWidget::RecreateItems(const LyShine::EntityArray& elements)
|
|
{
|
|
// remember the currently selected items so we can restore them
|
|
EntityHelpers::EntityIdList selectedEntityIds = SelectionHelpers::GetSelectedElementIds(this,
|
|
selectedItems(), false);
|
|
|
|
ClearItems();
|
|
|
|
CreateItems(elements);
|
|
|
|
HierarchyHelpers::SetSelectedItems(this, &selectedEntityIds);
|
|
}
|
|
|
|
void HierarchyWidget::ClearItems()
|
|
{
|
|
ClearAllHierarchyItemEntityIds();
|
|
|
|
// Remove all the items from the list (doesn't delete Entities since we cleared the EntityIds)
|
|
clear();
|
|
|
|
// The map needs to be cleared here since HandleItemRemove won't remove the map entry due to the entity Ids being cleared above
|
|
m_entityItemMap.clear();
|
|
|
|
m_isInited = false;
|
|
}
|
|
|
|
AZ::Entity* HierarchyWidget::CurrentSelectedElement() const
|
|
{
|
|
auto currentItem = HierarchyItem::RttiCast(QTreeWidget::currentItem());
|
|
AZ::Entity* currentElement = (currentItem && currentItem->isSelected()) ? currentItem->GetElement() : nullptr;
|
|
return currentElement;
|
|
}
|
|
|
|
void HierarchyWidget::contextMenuEvent(QContextMenuEvent* ev)
|
|
{
|
|
// The context menu.
|
|
if (m_isInited)
|
|
{
|
|
HierarchyMenu contextMenu(this,
|
|
(HierarchyMenu::Show::kCutCopyPaste |
|
|
HierarchyMenu::Show::kNew_EmptyElement |
|
|
HierarchyMenu::Show::kDeleteElement |
|
|
HierarchyMenu::Show::kNewSlice |
|
|
HierarchyMenu::Show::kNew_InstantiateSlice |
|
|
HierarchyMenu::Show::kPushToSlice |
|
|
HierarchyMenu::Show::kFindElements |
|
|
HierarchyMenu::Show::kEditorOnly),
|
|
true);
|
|
|
|
contextMenu.exec(ev->globalPos());
|
|
}
|
|
|
|
QTreeWidget::contextMenuEvent(ev);
|
|
}
|
|
|
|
void HierarchyWidget::SignalUserSelectionHasChanged(const QTreeWidgetItemRawPtrQList& selectedItems)
|
|
{
|
|
HierarchyItemRawPtrList items = SelectionHelpers::GetSelectedHierarchyItems(this,
|
|
selectedItems);
|
|
SetUserSelection(items.empty() ? nullptr : &items);
|
|
}
|
|
|
|
void HierarchyWidget::CurrentSelectionHasChanged([[maybe_unused]] const QItemSelection& selected,
|
|
[[maybe_unused]] const QItemSelection& deselected)
|
|
{
|
|
m_selectionChangedBeforeDrag = true;
|
|
|
|
// IMPORTANT: This signal is triggered at the right time, but
|
|
// "selected.indexes()" DOESN'T contain ALL the items currently
|
|
// selected. It ONLY contains the newly selected items. To avoid
|
|
// having to track what's added and removed to the selection,
|
|
// we'll use selectedItems().
|
|
|
|
if (m_signalSelectionChange && !m_isDeleting)
|
|
{
|
|
SignalUserSelectionHasChanged(selectedItems());
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::DataHasChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, [[maybe_unused]] const QVector<int>& roles)
|
|
{
|
|
if (topLeft == bottomRight)
|
|
{
|
|
// We only care about text changes, which can ONLY be done one at a
|
|
// time. This implies that topLeft must be the same as bottomRight.
|
|
|
|
HierarchyItem* hierarchyItem = HierarchyItem::RttiCast(itemFromIndex(topLeft));
|
|
AZ::Entity* element = hierarchyItem->GetElement();
|
|
AZ_Assert(element, "No entity found for hierarchy item");
|
|
AZ::EntityId entityId = element->GetId();
|
|
QTreeWidgetItem* item = HierarchyHelpers::ElementToItem(this, element, false);
|
|
QString toName(item ? item->text(0) : "");
|
|
|
|
CommandHierarchyItemRename::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
entityId,
|
|
element->GetName().c_str(),
|
|
toName);
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::HandleItemAdd(HierarchyItem* item)
|
|
{
|
|
m_entityItemMap[ item->GetEntityId() ] = item;
|
|
}
|
|
|
|
void HierarchyWidget::HandleItemRemove(HierarchyItem* item)
|
|
{
|
|
if (item == m_itemBeingHovered)
|
|
{
|
|
m_itemBeingHovered = nullptr;
|
|
}
|
|
|
|
m_entityItemMap.erase(item->GetEntityId());
|
|
}
|
|
|
|
void HierarchyWidget::ClearAllHierarchyItemEntityIds()
|
|
{
|
|
// as a simple way of going through all the HierarchyItem's we use the
|
|
// EntityHelpers::EntityToHierarchyItemMap
|
|
for (auto mapItem : m_entityItemMap)
|
|
{
|
|
mapItem.second->ClearEntityId();
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::ApplyElementIsExpanded()
|
|
{
|
|
// Seed the list.
|
|
HierarchyItemRawPtrList allItems;
|
|
HierarchyHelpers::AppendAllChildrenToEndOfList(invisibleRootItem(), allItems);
|
|
|
|
// Traverse the list.
|
|
blockSignals(true);
|
|
{
|
|
HierarchyHelpers::TraverseListAndAllChildren(allItems,
|
|
[](HierarchyItem* childItem)
|
|
{
|
|
childItem->ApplyElementIsExpanded();
|
|
});
|
|
}
|
|
blockSignals(false);
|
|
}
|
|
|
|
void HierarchyWidget::mousePressEvent(QMouseEvent* ev)
|
|
{
|
|
m_selectionChangedBeforeDrag = false;
|
|
|
|
HierarchyItem* item = HierarchyItem::RttiCast(itemAt(ev->pos()));
|
|
if (!item)
|
|
{
|
|
// This allows the user to UNSELECT an item
|
|
// by clicking in an empty area of the widget.
|
|
SetUniqueSelectionHighlight((QTreeWidgetItem*)nullptr);
|
|
}
|
|
|
|
// Remember the selected items before the selection change in case a drag is started.
|
|
// When dragging outside the hierarchy, the selection is reverted back to this selection
|
|
m_beforeDragSelection = selectedItems();
|
|
|
|
m_signalSelectionChange = false;
|
|
|
|
QTreeWidget::mousePressEvent(ev);
|
|
|
|
m_signalSelectionChange = true;
|
|
}
|
|
|
|
void HierarchyWidget::mouseDoubleClickEvent(QMouseEvent* ev)
|
|
{
|
|
HierarchyItem* item = HierarchyItem::RttiCast(itemAt(ev->pos()));
|
|
if (item)
|
|
{
|
|
// Double-clicking to edit text is only allowed in the FIRST column.
|
|
for (int col = kHierarchyColumnIsVisible; col < kHierarchyColumnCount; ++col)
|
|
{
|
|
QRect r = visualRect(indexFromItem(item, col));
|
|
if (r.contains(ev->pos()))
|
|
{
|
|
// Ignore the event.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
QTreeWidget::mouseDoubleClickEvent(ev);
|
|
}
|
|
|
|
void HierarchyWidget::startDrag(Qt::DropActions supportedActions)
|
|
{
|
|
// This flag is used to determine whether to perform an action on leaveEvent.
|
|
// If an item is dragged really fast outside the hierarchy, this startDrag event is called,
|
|
// but the dragEnterEvent and dragLeaveEvent are replaced with the leaveEvent
|
|
m_inDragStartState = true;
|
|
|
|
// Remember the current selection so that we can revert back to it when the items are dragged back into the hierarchy
|
|
m_dragSelection = selectedItems();
|
|
|
|
AzQtComponents::StyledTreeWidget::startDrag(supportedActions);
|
|
}
|
|
|
|
void HierarchyWidget::dragEnterEvent(QDragEnterEvent* event)
|
|
{
|
|
if (!AcceptsMimeData(event->mimeData()))
|
|
{
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
if (event->mimeData()->hasFormat(AzToolsFramework::EditorEntityIdContainer::GetMimeType()))
|
|
{
|
|
m_inDragStartState = false;
|
|
|
|
if (m_selectionChangedBeforeDrag)
|
|
{
|
|
m_signalSelectionChange = false;
|
|
|
|
// Set the current selection to the items being dragged
|
|
clearSelection();
|
|
for (auto i : m_dragSelection)
|
|
{
|
|
i->setSelected(true);
|
|
}
|
|
|
|
m_signalSelectionChange = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Dragging an item from outside the hierarchy window
|
|
m_selectionChangedBeforeDrag = false;
|
|
}
|
|
|
|
QTreeView::dragEnterEvent(event);
|
|
}
|
|
|
|
void HierarchyWidget::dragLeaveEvent(QDragLeaveEvent* event)
|
|
{
|
|
// This is called when dragging outside the hierarchy, or when a drag is released inside the hierarchy
|
|
// but a dropEvent isn't called (ex. drop item onto itself or press Esc to cancel a drag)
|
|
|
|
// Check if mouse position is inside or outside the hierarchy
|
|
QRect widgetRect = geometry();
|
|
QPoint mousePos = mapFromGlobal(QCursor::pos());
|
|
if (widgetRect.contains(mousePos))
|
|
{
|
|
if (m_selectionChangedBeforeDrag)
|
|
{
|
|
// Treat this event as a mouse release (mouseReleaseEvent is not called in this case)
|
|
SignalUserSelectionHasChanged(selectedItems());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_selectionChangedBeforeDrag)
|
|
{
|
|
m_signalSelectionChange = false;
|
|
|
|
// Set the current selection to the items that were selected before the drag
|
|
clearSelection();
|
|
for (auto i : m_beforeDragSelection)
|
|
{
|
|
i->setSelected(true);
|
|
}
|
|
|
|
m_signalSelectionChange = true;
|
|
}
|
|
}
|
|
|
|
QTreeView::dragLeaveEvent(event);
|
|
}
|
|
|
|
void HierarchyWidget::dropEvent(QDropEvent* ev)
|
|
{
|
|
if (!m_isInited)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ev->mimeData()->hasFormat(AzToolsFramework::EditorEntityIdContainer::GetMimeType()))
|
|
{
|
|
m_inDragStartState = false;
|
|
|
|
m_signalSelectionChange = false;
|
|
|
|
// Get a list of selected items
|
|
QTreeWidgetItemRawPtrQList selection = selectedItems();
|
|
|
|
// Change current selection to only contain top level items. This avoids
|
|
// the default drop behavior from changing the internal hierarchy of
|
|
// the dragged elements
|
|
QTreeWidgetItemRawPtrQList topLevelSelection;
|
|
SelectionHelpers::GetListOfTopLevelSelectedItems(this, selection, topLevelSelection);
|
|
clearSelection();
|
|
for (auto i : topLevelSelection)
|
|
{
|
|
i->setSelected(true);
|
|
}
|
|
|
|
// Set current parent and child index of each selected item
|
|
for (auto i : selection)
|
|
{
|
|
HierarchyItem* item = HierarchyItem::RttiCast(i);
|
|
if (item)
|
|
{
|
|
QModelIndex itemIndex = indexFromItem(item);
|
|
|
|
QTreeWidgetItem* baseParentItem = itemFromIndex(itemIndex.parent());
|
|
if (!baseParentItem)
|
|
{
|
|
baseParentItem = invisibleRootItem();
|
|
}
|
|
HierarchyItem* parentItem = HierarchyItem::RttiCast(baseParentItem);
|
|
AZ::EntityId parentId = (parentItem ? parentItem->GetEntityId() : AZ::EntityId());
|
|
|
|
item->SetPreMove(parentId, itemIndex.row());
|
|
}
|
|
}
|
|
|
|
// Do the drop event
|
|
ev->setDropAction(Qt::MoveAction);
|
|
QTreeWidget::dropEvent(ev);
|
|
|
|
// Make a list of selected items and their parents
|
|
HierarchyItemRawPtrList childItems;
|
|
QTreeWidgetItemRawPtrList baseParentItems;
|
|
|
|
bool itemMoved = false;
|
|
|
|
for (auto i : selection)
|
|
{
|
|
HierarchyItem* item = HierarchyItem::RttiCast(i);
|
|
if (item)
|
|
{
|
|
QModelIndex index = indexFromItem(item);
|
|
|
|
QTreeWidgetItem* baseParentItem = itemFromIndex(index.parent());
|
|
if (!baseParentItem)
|
|
{
|
|
baseParentItem = invisibleRootItem();
|
|
}
|
|
HierarchyItem* parentItem = HierarchyItem::RttiCast(baseParentItem);
|
|
AZ::EntityId parentId = parentItem ? parentItem->GetEntityId() : AZ::EntityId();
|
|
|
|
if ((item->GetPreMoveChildRow() != index.row()) || (item->GetPreMoveParentId() != parentId))
|
|
{
|
|
// Item has moved
|
|
itemMoved = true;
|
|
}
|
|
|
|
childItems.push_back(item);
|
|
baseParentItems.push_back(baseParentItem);
|
|
}
|
|
}
|
|
|
|
if (itemMoved)
|
|
{
|
|
ReparentItems(baseParentItems, childItems);
|
|
}
|
|
else
|
|
{
|
|
// Items didn't move, but they became unselected so they need to be reselected
|
|
for (auto i : childItems)
|
|
{
|
|
i->setSelected(true);
|
|
}
|
|
}
|
|
|
|
m_signalSelectionChange = true;
|
|
|
|
if (m_selectionChangedBeforeDrag)
|
|
{
|
|
// Signal a selection change on the mouse release
|
|
SignalUserSelectionHasChanged(selectedItems());
|
|
}
|
|
}
|
|
else if (AssetDropHelpers::DoesMimeDataContainSliceOrComponentAssets(ev->mimeData()))
|
|
{
|
|
DropMimeDataAssetsAtHierarchyPosition(ev->mimeData(), ev->pos());
|
|
|
|
ev->setDropAction(Qt::CopyAction);
|
|
ev->accept();
|
|
QTreeWidget::dropEvent(ev);
|
|
|
|
// Put focus on the hierarchy widget
|
|
activateWindow();
|
|
setFocus();
|
|
}
|
|
}
|
|
|
|
QStringList HierarchyWidget::mimeTypes() const
|
|
{
|
|
QStringList list = QTreeWidget::mimeTypes();
|
|
list.append(AzToolsFramework::EditorEntityIdContainer::GetMimeType());
|
|
list.append(AzToolsFramework::AssetBrowser::AssetBrowserEntry::GetMimeType());
|
|
return list;
|
|
}
|
|
|
|
QMimeData* HierarchyWidget::mimeData(const QList<QTreeWidgetItem*> items) const
|
|
{
|
|
AzToolsFramework::EditorEntityIdContainer entityIdList;
|
|
for (auto i : items)
|
|
{
|
|
HierarchyItem* item = HierarchyItem::RttiCast(i);
|
|
AZ::EntityId entityId = item->GetEntityId();
|
|
if (entityId.IsValid())
|
|
{
|
|
entityIdList.m_entityIds.push_back(entityId);
|
|
}
|
|
}
|
|
if (entityIdList.m_entityIds.empty())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
AZStd::vector<char> encoded;
|
|
if (!entityIdList.ToBuffer(encoded))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
QMimeData* mimeDataPtr = new QMimeData();
|
|
QByteArray encodedData;
|
|
encodedData.resize((int)encoded.size());
|
|
memcpy(encodedData.data(), encoded.data(), encoded.size());
|
|
|
|
mimeDataPtr->setData(AzToolsFramework::EditorEntityIdContainer::GetMimeType(), encodedData);
|
|
return mimeDataPtr;
|
|
}
|
|
|
|
bool HierarchyWidget::AcceptsMimeData(const QMimeData* mimeData)
|
|
{
|
|
if (!mimeData)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!m_isInited)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (mimeData->hasFormat(AzToolsFramework::EditorEntityIdContainer::GetMimeType()))
|
|
{
|
|
QByteArray arrayData = mimeData->data(AzToolsFramework::EditorEntityIdContainer::GetMimeType());
|
|
|
|
AzToolsFramework::EditorEntityIdContainer entityIdListContainer;
|
|
if (!entityIdListContainer.FromBuffer(arrayData.constData(), arrayData.size()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (entityIdListContainer.m_entityIds.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get the entity context that the first dragged entity is attached to
|
|
AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
|
|
EBUS_EVENT_ID_RESULT(contextId, entityIdListContainer.m_entityIds[0], AzFramework::EntityIdContextQueryBus, GetOwningContextId);
|
|
if (contextId.IsNull())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check that the entity context is the UI editor entity context
|
|
UiEditorEntityContext* editorEntityContext = m_editorWindow->GetEntityContext();
|
|
if (!editorEntityContext || (editorEntityContext->GetContextId() != contextId))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return AssetDropHelpers::DoesMimeDataContainSliceOrComponentAssets(mimeData);
|
|
}
|
|
|
|
void HierarchyWidget::DropMimeDataAssetsAtHierarchyPosition(const QMimeData* mimeData, const QPoint& position)
|
|
{
|
|
using namespace AzToolsFramework;
|
|
|
|
// Check where the drop indicator is to determine the parent for a new entity
|
|
// or to determine an existing entity for new components
|
|
QTreeWidgetItem* item = itemAt(position);
|
|
DropIndicatorPosition dropPosition = dropIndicatorPosition();
|
|
|
|
QTreeWidgetItem* targetWidgetItem = nullptr;
|
|
bool onItem = false;
|
|
int childIndex = -1;
|
|
switch (dropPosition)
|
|
{
|
|
case QAbstractItemView::AboveItem:
|
|
targetWidgetItem = item->parent();
|
|
childIndex = (targetWidgetItem ? targetWidgetItem : invisibleRootItem())->indexOfChild(item);
|
|
break;
|
|
case QAbstractItemView::BelowItem:
|
|
targetWidgetItem = item->parent();
|
|
childIndex = (targetWidgetItem ? targetWidgetItem : invisibleRootItem())->indexOfChild(item) + 1;
|
|
break;
|
|
case QAbstractItemView::OnItem:
|
|
targetWidgetItem = item;
|
|
// Shift modifier enables creating a child entity from the asset
|
|
onItem = !(QApplication::keyboardModifiers() & Qt::ShiftModifier);
|
|
break;
|
|
case QAbstractItemView::OnViewport:
|
|
targetWidgetItem = nullptr;
|
|
break;
|
|
}
|
|
|
|
DropMimeDataAssets(mimeData, targetWidgetItem, onItem, childIndex, nullptr);
|
|
}
|
|
|
|
void HierarchyWidget::DropMimeDataAssets(const QMimeData* mimeData,
|
|
QTreeWidgetItem* targetWidgetItem,
|
|
bool onElement,
|
|
int childIndex,
|
|
const QPoint* newElementPosition)
|
|
{
|
|
ComponentAssetHelpers::ComponentAssetPairs componentAssetPairs;
|
|
AssetDropHelpers::AssetList sliceAssets;
|
|
AssetDropHelpers::DecodeSliceAndComponentAssetsFromMimeData(mimeData, componentAssetPairs, sliceAssets);
|
|
|
|
if (componentAssetPairs.empty() && sliceAssets.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Change current selection so instantiated slices will be parented correctly
|
|
if (targetWidgetItem)
|
|
{
|
|
SetUniqueSelectionHighlight(targetWidgetItem);
|
|
}
|
|
else
|
|
{
|
|
clearSelection();
|
|
}
|
|
|
|
// Instantiate dropped slices
|
|
for (const AZ::Data::AssetId& sliceAssetId : sliceAssets)
|
|
{
|
|
// Instantiate slice under currently selected parent
|
|
AZ::Vector2 viewportPosition(-1.0f, -1.0f); // indicates no viewport position specified
|
|
if (newElementPosition)
|
|
{
|
|
viewportPosition = QtHelpers::QPointFToVector2(*newElementPosition);
|
|
}
|
|
GetEditorWindow()->GetSliceManager()->InstantiateSlice(sliceAssetId, viewportPosition, childIndex);
|
|
}
|
|
|
|
if (componentAssetPairs.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Add components to the element being hovered or to a newly created element
|
|
if (onElement)
|
|
{
|
|
// Add components to the existing target element which is now the selected element
|
|
AZ_Assert(targetWidgetItem, "Must provide a target item when dropping component assets onto an element");
|
|
|
|
// Make a list of the component types to be added
|
|
AZStd::vector<AZ::TypeId> componentTypes;
|
|
componentTypes.reserve(componentAssetPairs.size());
|
|
for (const ComponentAssetHelpers::ComponentAssetPair& pair : componentAssetPairs)
|
|
{
|
|
componentTypes.push_back(pair.first);
|
|
}
|
|
|
|
ComponentHelpers::EntityComponentPair firstIncompatibleComponentType = AZStd::make_pair(AZ::EntityId(), AZ::Uuid::CreateNull());
|
|
if (!ComponentHelpers::CanAddComponentsToSelectedEntities(componentTypes, &firstIncompatibleComponentType))
|
|
{
|
|
const AZ::EntityId& entityId = firstIncompatibleComponentType.first;
|
|
const AZ::TypeId& componentTypeId = firstIncompatibleComponentType.second;
|
|
|
|
HierarchyItem* targetItem = HierarchyItem::RttiCast(targetWidgetItem);
|
|
AZStd::string entityName(targetItem->GetElement() ? targetItem->GetElement()->GetName() : "<unknown>");
|
|
|
|
if (!entityId.IsValid() || componentTypeId.IsNull())
|
|
{
|
|
const AZStd::string message = AZStd::string::format("Failed to add components to target element \"%s\".", entityName.c_str());
|
|
QMessageBox::warning(m_editorWindow, tr("Asset Drop"), QString(message.c_str()));
|
|
}
|
|
else
|
|
{
|
|
AZ::ComponentDescriptor* descriptor = nullptr;
|
|
AZ::ComponentDescriptorBus::EventResult(descriptor, firstIncompatibleComponentType.second, &AZ::ComponentDescriptorBus::Events::GetDescriptor);
|
|
AZStd::string componentName(descriptor ? descriptor->GetName() : "<unknown>");
|
|
const AZStd::string message = AZStd::string::format("Failed to add components to target element \"%s\". Component \"%s\" is not compatible.", entityName.c_str(), componentName.c_str());
|
|
QMessageBox::warning(m_editorWindow, tr("Asset Drop"), QString(message.c_str()));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Batch-add all the components
|
|
ComponentHelpers::AddComponentsWithAssetToSelectedEntities(componentAssetPairs);
|
|
}
|
|
else
|
|
{
|
|
// Create a new element
|
|
QTreeWidgetItemRawPtrQList parentItems;
|
|
if (targetWidgetItem)
|
|
{
|
|
parentItems.append(targetWidgetItem);
|
|
}
|
|
CommandHierarchyItemCreate::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
parentItems,
|
|
childIndex,
|
|
[this, componentAssetPairs, newElementPosition](AZ::Entity* element)
|
|
{
|
|
if (element)
|
|
{
|
|
// Set the element's position
|
|
if (newElementPosition)
|
|
{
|
|
EntityHelpers::MoveElementToGlobalPosition(element, *newElementPosition);
|
|
}
|
|
|
|
// Make a list of the component types to be added
|
|
AZStd::vector<AZ::TypeId> componentTypes;
|
|
componentTypes.reserve(componentAssetPairs.size());
|
|
for (const ComponentAssetHelpers::ComponentAssetPair& pair : componentAssetPairs)
|
|
{
|
|
componentTypes.push_back(pair.first);
|
|
}
|
|
|
|
ComponentHelpers::EntityComponentPair firstIncompatibleComponentType = AZStd::make_pair(AZ::EntityId(), AZ::Uuid::CreateNull());
|
|
if (!ComponentHelpers::CanAddComponentsToEntity(componentTypes, element->GetId(), &firstIncompatibleComponentType))
|
|
{
|
|
const AZ::TypeId& componentTypeId = firstIncompatibleComponentType.second;
|
|
if (componentTypeId.IsNull())
|
|
{
|
|
QMessageBox::warning(m_editorWindow, tr("Asset Drop"), tr("Failed to add components to new element."));
|
|
}
|
|
else
|
|
{
|
|
AZ::ComponentDescriptor* descriptor = nullptr;
|
|
AZ::ComponentDescriptorBus::EventResult(descriptor, firstIncompatibleComponentType.second, &AZ::ComponentDescriptorBus::Events::GetDescriptor);
|
|
AZStd::string componentName(descriptor ? descriptor->GetName() : "<unknown>");
|
|
const AZStd::string message = AZStd::string::format("Failed to add components to new element. Component \"%s\" is not compatible.", componentName.c_str());
|
|
QMessageBox::warning(m_editorWindow, tr("Asset Drop"), QString(message.c_str()));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Batch-add all the components
|
|
ComponentHelpers::AddComponentsWithAssetToEntity(componentAssetPairs, element->GetId());
|
|
|
|
// Name the entity after the first asset
|
|
const ComponentAssetHelpers::ComponentAssetPair& pair = componentAssetPairs.front();
|
|
const AZ::Data::AssetId& assetId = pair.second;
|
|
AZStd::string assetPath;
|
|
EBUS_EVENT_RESULT(assetPath, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, assetId);
|
|
if (!assetPath.empty())
|
|
{
|
|
AZStd::string entityName;
|
|
AzFramework::StringFunc::Path::GetFileName(assetPath.c_str(), entityName);
|
|
|
|
// Find a unique name for the new element
|
|
AZ::EntityId parentEntityId;
|
|
EBUS_EVENT_ID_RESULT(parentEntityId, element->GetId(), UiElementBus, GetParentEntityId);
|
|
|
|
AZStd::string uniqueName;
|
|
EBUS_EVENT_ID_RESULT(uniqueName,
|
|
GetEditorWindow()->GetCanvas(),
|
|
UiCanvasBus,
|
|
GetUniqueChildName,
|
|
parentEntityId,
|
|
entityName,
|
|
nullptr);
|
|
|
|
element->SetName(uniqueName);
|
|
|
|
QTreeWidgetItem* item = HierarchyHelpers::ElementToItem(this, element, false);
|
|
AZ_Assert(item, "Newly created element doesn't have a hierarchy item");
|
|
item->setText(0, uniqueName.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::warning(m_editorWindow, tr("Asset Drop"), tr("Failed to create a new element to add components to."));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::mouseMoveEvent(QMouseEvent* ev)
|
|
{
|
|
HierarchyItem* itemBeingHovered = HierarchyItem::RttiCast(itemAt(ev->pos()));
|
|
if (itemBeingHovered)
|
|
{
|
|
// Hovering.
|
|
|
|
if (m_itemBeingHovered)
|
|
{
|
|
if (itemBeingHovered == m_itemBeingHovered)
|
|
{
|
|
// Still hovering over the same item.
|
|
// Nothing to do.
|
|
}
|
|
else
|
|
{
|
|
// Hover start over a different item.
|
|
|
|
// Hover ends over the previous item.
|
|
m_itemBeingHovered->SetMouseIsHovering(false);
|
|
|
|
// Hover starts over the current item.
|
|
m_itemBeingHovered = itemBeingHovered;
|
|
m_itemBeingHovered->SetMouseIsHovering(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Hover start.
|
|
m_itemBeingHovered = itemBeingHovered;
|
|
m_itemBeingHovered->SetMouseIsHovering(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not hovering.
|
|
|
|
if (m_itemBeingHovered)
|
|
{
|
|
// Hover end.
|
|
m_itemBeingHovered->SetMouseIsHovering(false);
|
|
m_itemBeingHovered = nullptr;
|
|
}
|
|
else
|
|
{
|
|
// Still not hovering.
|
|
// Nothing to do.
|
|
}
|
|
}
|
|
|
|
QTreeWidget::mouseMoveEvent(ev);
|
|
}
|
|
|
|
void HierarchyWidget::mouseReleaseEvent(QMouseEvent* ev)
|
|
{
|
|
if (m_selectionChangedBeforeDrag)
|
|
{
|
|
// Signal a selection change on the mouse release
|
|
SignalUserSelectionHasChanged(selectedItems());
|
|
}
|
|
|
|
QTreeWidget::mouseReleaseEvent(ev);
|
|
|
|
// In pick mode, the user can click on an item and drag the mouse to change the current item.
|
|
// In this case, a click event is not sent on a mouse release, so set the current item as the
|
|
// picked item here
|
|
if (m_inObjectPickMode)
|
|
{
|
|
// If there is a current item, set that as picked
|
|
if (currentIndex() != QModelIndex()) // check for a valid index
|
|
{
|
|
QTreeWidgetItem* item = itemFromIndex(currentIndex());
|
|
if (item)
|
|
{
|
|
PickItem(HierarchyItem::RttiCast(item));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::leaveEvent(QEvent* ev)
|
|
{
|
|
ClearItemBeingHovered();
|
|
|
|
// If an item is dragged really fast outside the hierarchy, the startDrag event is called,
|
|
// but the dragEnterEvent and dragLeaveEvent are replaced with the leaveEvent.
|
|
// In this case, perform the dragLeaveEvent here
|
|
if (m_inDragStartState)
|
|
{
|
|
if (m_selectionChangedBeforeDrag)
|
|
{
|
|
m_signalSelectionChange = false;
|
|
|
|
// Set the current selection to the items that were selected before the drag
|
|
clearSelection();
|
|
for (auto i : m_beforeDragSelection)
|
|
{
|
|
i->setSelected(true);
|
|
}
|
|
|
|
m_signalSelectionChange = true;
|
|
}
|
|
|
|
m_inDragStartState = false;
|
|
}
|
|
|
|
QTreeWidget::leaveEvent(ev);
|
|
}
|
|
|
|
void HierarchyWidget::ClearItemBeingHovered()
|
|
{
|
|
if (!m_itemBeingHovered)
|
|
{
|
|
// Nothing to do.
|
|
return;
|
|
}
|
|
|
|
m_itemBeingHovered->SetMouseIsHovering(false);
|
|
m_itemBeingHovered = nullptr;
|
|
}
|
|
|
|
void HierarchyWidget::UpdateSliceInfo()
|
|
{
|
|
// Update the slice information (color, font, tooltip) for all elements.
|
|
// As a simple way of going through all the HierarchyItem's we use the
|
|
// EntityHelpers::EntityToHierarchyItemMap
|
|
for (auto mapItem : m_entityItemMap)
|
|
{
|
|
mapItem.second->UpdateSliceInfo();
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::DropMimeDataAssets(const QMimeData* mimeData,
|
|
const AZ::EntityId& targetEntityId,
|
|
bool onElement,
|
|
int childIndex,
|
|
const QPoint* newElementPosition)
|
|
{
|
|
if (!m_isInited)
|
|
{
|
|
return;
|
|
}
|
|
|
|
QTreeWidgetItem* targetWidgetItem = targetEntityId.IsValid() ? HierarchyHelpers::ElementToItem(this, targetEntityId, false) : nullptr;
|
|
DropMimeDataAssets(mimeData, targetWidgetItem, onElement, childIndex, newElementPosition);
|
|
}
|
|
|
|
void HierarchyWidget::DeleteSelectedItems()
|
|
{
|
|
DeleteSelectedItems(selectedItems());
|
|
}
|
|
|
|
void HierarchyWidget::OnEntityPickModeStarted()
|
|
{
|
|
setDragEnabled(false);
|
|
m_currentItemBeforePickMode = currentIndex();
|
|
m_selectionModeBeforePickMode = selectionMode();
|
|
setSelectionMode(QAbstractItemView::NoSelection);
|
|
m_editTriggersBeforePickMode = editTriggers();
|
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
setCursor(m_editorWindow->GetEntityPickerCursor());
|
|
m_inObjectPickMode = true;
|
|
}
|
|
|
|
void HierarchyWidget::OnEntityPickModeStopped()
|
|
{
|
|
if (m_inObjectPickMode)
|
|
{
|
|
setCurrentIndex(m_currentItemBeforePickMode);
|
|
setDragEnabled(true);
|
|
setSelectionMode(m_selectionModeBeforePickMode);
|
|
setEditTriggers(m_editTriggersBeforePickMode);
|
|
setCursor(Qt::ArrowCursor);
|
|
m_inObjectPickMode = false;
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::EntityHighlightRequested([[maybe_unused]] AZ::EntityId entityId)
|
|
{
|
|
}
|
|
|
|
void HierarchyWidget::EntityStrongHighlightRequested(AZ::EntityId entityId)
|
|
{
|
|
// Check if this entity is in the same entity context
|
|
if (!IsEntityInEntityContext(entityId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
QTreeWidgetItem* item = HierarchyHelpers::ElementToItem(this, entityId, false);
|
|
if (!item)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Scrolling to the entity will make sure that it is visible.
|
|
// This will automatically open parents
|
|
scrollToItem(item);
|
|
|
|
// Select the entity
|
|
SetUniqueSelectionHighlight(item);
|
|
}
|
|
|
|
void HierarchyWidget::PickItem(HierarchyItem* item)
|
|
{
|
|
const AZ::EntityId entityId = item->GetEntityId();
|
|
if (entityId.IsValid())
|
|
{
|
|
AzToolsFramework::EditorPickModeRequestBus::Broadcast(
|
|
&AzToolsFramework::EditorPickModeRequests::PickModeSelectEntity, entityId);
|
|
|
|
AzToolsFramework::EditorPickModeRequestBus::Broadcast(
|
|
&AzToolsFramework::EditorPickModeRequests::StopEntityPickMode);
|
|
}
|
|
}
|
|
|
|
bool HierarchyWidget::IsEntityInEntityContext(AZ::EntityId entityId)
|
|
{
|
|
AzFramework::EntityContextId contextId = AzFramework::EntityContextId::CreateNull();
|
|
EBUS_EVENT_ID_RESULT(contextId, entityId, AzFramework::EntityIdContextQueryBus, GetOwningContextId);
|
|
|
|
if (!contextId.IsNull())
|
|
{
|
|
UiEditorEntityContext* editorEntityContext = m_editorWindow->GetEntityContext();
|
|
if (editorEntityContext && editorEntityContext->GetContextId() == contextId)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void HierarchyWidget::ReparentItems(const QTreeWidgetItemRawPtrList& baseParentItems,
|
|
const HierarchyItemRawPtrList& childItems)
|
|
{
|
|
CommandHierarchyItemReparent::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
childItems,
|
|
baseParentItems);
|
|
}
|
|
|
|
void HierarchyWidget::ToggleVisibility(HierarchyItem* hierarchyItem)
|
|
{
|
|
bool isItemVisible = true;
|
|
AZ::EntityId itemEntityId = hierarchyItem->GetEntityId();
|
|
EBUS_EVENT_ID_RESULT(isItemVisible, itemEntityId, UiEditorBus, GetIsVisible);
|
|
|
|
// There is one exception to toggling the visibility. If the clicked item has invisible ancestors,
|
|
// then we make that item and all its ancestors visible regardless of the item's visibility
|
|
|
|
// Make a list of items to modify
|
|
HierarchyItemRawPtrList items;
|
|
|
|
// Look for invisible ancestors
|
|
AZ::EntityId parent;
|
|
EBUS_EVENT_ID_RESULT(parent, itemEntityId, UiElementBus, GetParentEntityId);
|
|
while (parent.IsValid())
|
|
{
|
|
bool isParentVisible = true;
|
|
EBUS_EVENT_ID_RESULT(isParentVisible, parent, UiEditorBus, GetIsVisible);
|
|
|
|
if (!isParentVisible)
|
|
{
|
|
items.push_back(m_entityItemMap[parent]);
|
|
}
|
|
|
|
AZ::EntityId newParent = parent;
|
|
parent.SetInvalid();
|
|
EBUS_EVENT_ID_RESULT(parent, newParent, UiElementBus, GetParentEntityId);
|
|
}
|
|
|
|
bool makeVisible = items.size() > 0 ? true : !isItemVisible;
|
|
|
|
// Add the item that was clicked
|
|
if (makeVisible != isItemVisible)
|
|
{
|
|
items.push_back(m_entityItemMap[itemEntityId]);
|
|
}
|
|
|
|
CommandHierarchyItemToggleIsVisible::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
items);
|
|
}
|
|
|
|
void HierarchyWidget::DeleteSelectedItems(const QTreeWidgetItemRawPtrQList& selectedItems)
|
|
{
|
|
CommandHierarchyItemDelete::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
selectedItems);
|
|
|
|
// This ensures there's no "current item".
|
|
SetUniqueSelectionHighlight((QTreeWidgetItem*)nullptr);
|
|
|
|
// IMPORTANT: This is necessary to indirectly trigger detach()
|
|
// in the PropertiesWidget.
|
|
SetUserSelection(nullptr);
|
|
}
|
|
|
|
void HierarchyWidget::Cut()
|
|
{
|
|
QTreeWidgetItemRawPtrQList selection = selectedItems();
|
|
|
|
HierarchyClipboard::CopySelectedItemsToClipboard(this,
|
|
selection);
|
|
DeleteSelectedItems(selection);
|
|
}
|
|
|
|
void HierarchyWidget::Copy()
|
|
{
|
|
HierarchyClipboard::CopySelectedItemsToClipboard(this,
|
|
selectedItems());
|
|
}
|
|
|
|
void HierarchyWidget::PasteAsSibling()
|
|
{
|
|
HierarchyClipboard::CreateElementsFromClipboard(this,
|
|
selectedItems(),
|
|
false);
|
|
}
|
|
|
|
void HierarchyWidget::PasteAsChild()
|
|
{
|
|
HierarchyClipboard::CreateElementsFromClipboard(this,
|
|
selectedItems(),
|
|
true);
|
|
}
|
|
|
|
void HierarchyWidget::SetEditorOnlyForSelectedItems(bool editorOnly)
|
|
{
|
|
QTreeWidgetItemRawPtrQList selection = selectedItems();
|
|
if (!selection.empty())
|
|
{
|
|
SerializeHelpers::SerializedEntryList preChangeState;
|
|
HierarchyClipboard::BeginUndoableEntitiesChange(m_editorWindow, preChangeState);
|
|
|
|
for (auto item : selection)
|
|
{
|
|
HierarchyItem* i = HierarchyItem::RttiCast(item);
|
|
|
|
AzToolsFramework::EditorOnlyEntityComponentRequestBus::Event(i->GetEntityId(), &AzToolsFramework::EditorOnlyEntityComponentRequests::SetIsEditorOnlyEntity, editorOnly);
|
|
|
|
i->UpdateEditorOnlyInfo();
|
|
}
|
|
|
|
HierarchyClipboard::EndUndoableEntitiesChange(m_editorWindow, "editor only selection", preChangeState);
|
|
|
|
emit editorOnlyStateChangedOnSelectedElements();
|
|
}
|
|
}
|
|
|
|
void HierarchyWidget::AddElement(const QTreeWidgetItemRawPtrQList& selectedItems, const QPoint* optionalPos)
|
|
{
|
|
const int childIndex = -1;
|
|
CommandHierarchyItemCreate::Push(m_editorWindow->GetActiveStack(),
|
|
this,
|
|
selectedItems,
|
|
childIndex,
|
|
[this, optionalPos](AZ::Entity* element)
|
|
{
|
|
if (optionalPos)
|
|
{
|
|
// Convert position to render viewport coords
|
|
QPoint scaledPosition = *optionalPos * GetEditorWindow()->GetViewport()->WidgetToViewportFactor();
|
|
EntityHelpers::MoveElementToGlobalPosition(element, scaledPosition);
|
|
}
|
|
});
|
|
}
|
|
|
|
void HierarchyWidget::SetUniqueSelectionHighlight(QTreeWidgetItem* item)
|
|
{
|
|
// Stop object pick mode when an action explicitly wants to set the hierarchy's selected items
|
|
AzToolsFramework::EditorPickModeRequestBus::Broadcast(
|
|
&AzToolsFramework::EditorPickModeRequests::StopEntityPickMode);
|
|
|
|
clearSelection();
|
|
|
|
setCurrentIndex(indexFromItem(item));
|
|
}
|
|
|
|
void HierarchyWidget::SetUniqueSelectionHighlight(const AZ::Entity* element)
|
|
{
|
|
SetUniqueSelectionHighlight(HierarchyHelpers::ElementToItem(this, element, false));
|
|
}
|
|
|
|
#include <moc_HierarchyWidget.cpp>
|