123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- /*
- * 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 <AzToolsFramework/Slice/SliceUtilities.h>
- #include <AzToolsFramework/ToolsComponents/EditorOnlyEntityComponentBus.h>
- #include "SliceMenuHelpers.h"
- #include "QtHelpers.h"
- // Define for enabling/disabling the UI Slice system
- #define ENABLE_UI_SLICE_MENU_ITEMS 1
- HierarchyMenu::HierarchyMenu(HierarchyWidget* hierarchy,
- size_t showMask,
- bool addMenuForNewElement,
- const QPoint* optionalPos)
- : QMenu()
- {
- setStyleSheet(UICANVASEDITOR_QMENU_ITEM_DISABLED_STYLESHEET);
- QTreeWidgetItemRawPtrQList selectedItems = hierarchy->selectedItems();
- if (showMask & (Show::kNew_EmptyElement | Show::kNew_EmptyElementAtRoot))
- {
- QMenu* menu = (addMenuForNewElement ? addMenu("&New...") : this);
- if (showMask & (Show::kNew_EmptyElement | Show::kNew_EmptyElementAtRoot))
- {
- New_EmptyElement(hierarchy, selectedItems, menu, (showMask & Show::kNew_EmptyElementAtRoot), optionalPos);
- }
- if (showMask & (Show::kNew_InstantiateSlice | Show::kNew_InstantiateSliceAtRoot))
- {
- New_ElementFromSlice(hierarchy, selectedItems, menu, (showMask & Show::kNew_InstantiateSliceAtRoot), optionalPos);
- }
- }
- if (showMask & (Show::kNewSlice | Show::kPushToSlice))
- {
- SliceMenuItems(hierarchy, selectedItems, showMask);
- }
- addSeparator();
- if (showMask & Show::kCutCopyPaste)
- {
- CutCopyPaste(hierarchy, selectedItems);
- }
- if (showMask & Show::kDeleteElement)
- {
- DeleteElement(hierarchy, selectedItems);
- }
- addSeparator();
- if (showMask & Show::kAddComponents)
- {
- AddComponents(hierarchy, selectedItems);
- }
- addSeparator();
- if (showMask & Show::kFindElements)
- {
- FindElements(hierarchy, selectedItems);
- }
- addSeparator();
- if (showMask & Show::kEditorOnly)
- {
- EditorOnly(hierarchy, selectedItems);
- }
- }
- void HierarchyMenu::CutCopyPaste(HierarchyWidget* hierarchy,
- QTreeWidgetItemRawPtrQList& selectedItems)
- {
- QAction* action;
- bool itemsAreSelected = (!selectedItems.isEmpty());
- // Cut element.
- {
- action = new QAction("Cut", this);
- action->setShortcut(QKeySequence::Cut);
- action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- QObject::connect(action,
- &QAction::triggered,
- hierarchy,
- [ hierarchy ]([[maybe_unused]] bool checked)
- {
- QMetaObject::invokeMethod(hierarchy, "Cut", Qt::QueuedConnection);
- });
- addAction(action);
- if (!itemsAreSelected)
- {
- // Nothing has been selected.
- // We want the menu to be visible, but disabled.
- action->setEnabled(false);
- }
- }
- // Copy element.
- {
- action = new QAction("Copy", this);
- action->setShortcut(QKeySequence::Copy);
- action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- QObject::connect(action,
- &QAction::triggered,
- hierarchy,
- [ hierarchy ]([[maybe_unused]] bool checked)
- {
- QMetaObject::invokeMethod(hierarchy, "Copy", Qt::QueuedConnection);
- });
- addAction(action);
- if (!itemsAreSelected)
- {
- // Nothing has been selected.
- // We want the menu to be visible, but disabled.
- action->setEnabled(false);
- }
- }
- bool thereIsContentInTheClipboard = ClipboardContainsOurDataType();
- // Paste element.
- {
- action = new QAction(QIcon(":/Icons/Eye_Open.png"), (itemsAreSelected ? "Paste as sibling" : "Paste"), this);
- action->setShortcut(QKeySequence::Paste);
- action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- QObject::connect(action,
- &QAction::triggered,
- hierarchy,
- [ hierarchy ]([[maybe_unused]] bool checked)
- {
- QMetaObject::invokeMethod(hierarchy, "PasteAsSibling", Qt::QueuedConnection);
- });
- addAction(action);
- if (!thereIsContentInTheClipboard)
- {
- // Nothing in the clipboard.
- // We want the menu to be visible, but disabled.
- action->setEnabled(false);
- }
- if (itemsAreSelected)
- {
- action = new QAction(QIcon(":/Icons/Eye_Open.png"), ("Paste as child"), this);
- {
- action->setShortcuts(QList<QKeySequence>{QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V),
- QKeySequence(Qt::META + Qt::SHIFT + Qt::Key_V)});
- action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- }
- QObject::connect(action,
- &QAction::triggered,
- hierarchy,
- [hierarchy]([[maybe_unused]] bool checked)
- {
- QMetaObject::invokeMethod(hierarchy, "PasteAsChild", Qt::QueuedConnection);
- });
- addAction(action);
- if (!thereIsContentInTheClipboard)
- {
- // Nothing in the clipboard.
- // We want the menu to be visible, but disabled.
- action->setEnabled(false);
- }
- }
- }
- }
- void HierarchyMenu::SliceMenuItems(HierarchyWidget* hierarchy,
- QTreeWidgetItemRawPtrQList& selectedItems,
- size_t showMask)
- {
- #if ENABLE_UI_SLICE_MENU_ITEMS
- // Get the EntityId's of the selected elements
- auto selectedEntities = SelectionHelpers::GetSelectedElementIds(hierarchy, selectedItems, false);
- // Determine if any of the selected entities are in a slice
- AZ::SliceComponent::EntityAncestorList referenceAncestors;
- AZStd::vector<AZ::SliceComponent::SliceInstanceAddress> sliceInstances;
- for (const AZ::EntityId& entityId : selectedEntities)
- {
- AZ::SliceComponent::SliceInstanceAddress sliceAddress;
- AzFramework::SliceEntityRequestBus::EventResult(sliceAddress, entityId,
- &AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
- if (sliceAddress.IsValid())
- {
- if (sliceInstances.end() == AZStd::find(sliceInstances.begin(), sliceInstances.end(), sliceAddress))
- {
- if (sliceInstances.empty())
- {
- sliceAddress.GetReference()->GetInstanceEntityAncestry(entityId, referenceAncestors);
- }
- sliceInstances.push_back(sliceAddress);
- }
- }
- }
- bool sliceSelected = sliceInstances.size() > 0;
- if (sliceSelected)
- {
- if (showMask & Show::kPushToSlice)
- {
- // Push slice action currently acts on entities and all descendants, so include those as part of the selection
- AzToolsFramework::EntityIdSet selectedTransformHierarchyEntities =
- hierarchy->GetEditorWindow()->GetSliceManager()->GatherEntitiesAndAllDescendents(selectedEntities);
- AzToolsFramework::EntityIdList selectedPushEntities;
- selectedPushEntities.insert(selectedPushEntities.begin(), selectedTransformHierarchyEntities.begin(), selectedTransformHierarchyEntities.end());
- QAction* action = addAction(QObject::tr("&Push to Slice..."));
- QObject::connect(action, &QAction::triggered, hierarchy, [hierarchy, selectedPushEntities]
- {
- hierarchy->GetEditorWindow()->GetSliceManager()->PushEntitiesModal(selectedPushEntities, nullptr);
- }
- );
- }
- if (showMask & Show::kNewSlice)
- {
- QAction* action = addAction("Make Cascaded Slice from Selected Slices && Entities...");
- QObject::connect(action, &QAction::triggered, hierarchy, [hierarchy]
- {
- hierarchy->GetEditorWindow()->GetSliceManager()->MakeSliceFromSelectedItems(hierarchy, true);
- }
- );
- action = addAction(QObject::tr("Make Detached Slice from Selected Entities..."));
- QObject::connect(action, &QAction::triggered, hierarchy, [hierarchy]
- {
- hierarchy->GetEditorWindow()->GetSliceManager()->MakeSliceFromSelectedItems(hierarchy, false);
- }
- );
- }
- if (showMask & Show::kPushToSlice) // use the push to slice flag to show detach since it appears in all the same situations
- {
- // Detach slice entity
- {
- // Detach entities action currently acts on entities and all descendants, so include those as part of the selection
- AzToolsFramework::EntityIdSet selectedTransformHierarchyEntities =
- hierarchy->GetEditorWindow()->GetSliceManager()->GatherEntitiesAndAllDescendents(selectedEntities);
- AzToolsFramework::EntityIdList selectedDetachEntities;
- selectedDetachEntities.insert(selectedDetachEntities.begin(), selectedTransformHierarchyEntities.begin(), selectedTransformHierarchyEntities.end());
- QString detachEntitiesActionText;
- if (selectedDetachEntities.size() == 1)
- {
- detachEntitiesActionText = QObject::tr("Detach slice entity...");
- }
- else
- {
- detachEntitiesActionText = QObject::tr("Detach slice entities...");
- }
- QAction* action = addAction(detachEntitiesActionText);
- QObject::connect(action, &QAction::triggered, hierarchy, [hierarchy, selectedDetachEntities]
- {
- hierarchy->GetEditorWindow()->GetSliceManager()->DetachSliceEntities(selectedDetachEntities);
- hierarchy->UpdateSliceInfo();
- }
- );
- }
- // Detach slice instance
- {
- QString detachSlicesActionText;
- if (sliceInstances.size() == 1)
- {
- detachSlicesActionText = QObject::tr("Detach slice instance...");
- }
- else
- {
- detachSlicesActionText = QObject::tr("Detach slice instances...");
- }
- QAction* action = addAction(detachSlicesActionText);
- QObject::connect(action, &QAction::triggered, hierarchy, [hierarchy, selectedEntities]
- {
- hierarchy->GetEditorWindow()->GetSliceManager()->DetachSliceInstances(selectedEntities);
- hierarchy->UpdateSliceInfo();
- }
- );
- }
- // Edit slice in new tab
- {
- QMenu* menu = addMenu("Edit slice in new tab");
- // Catalog all unique slices to which any of the selected entities are associated (anywhere in their ancestry).
- // This is used to make a menu allowing any of them to be edited in a new tab
- AZStd::vector<AZ::Data::AssetId> slicesAddedToMenu;
- AZ::SliceComponent::EntityAncestorList tempAncestors;
- for (AZ::EntityId entityId : selectedEntities)
- {
- AZ::SliceComponent::SliceInstanceAddress sliceAddress;
- AzFramework::SliceEntityRequestBus::EventResult(sliceAddress, entityId,
- &AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
- if (sliceAddress.IsValid())
- {
- tempAncestors.clear();
- sliceAddress.GetReference()->GetInstanceEntityAncestry(entityId, tempAncestors);
- for (const AZ::SliceComponent::Ancestor& ancestor : tempAncestors)
- {
- const AZ::Data::Asset<AZ::SliceAsset>& sliceAsset = ancestor.m_sliceAddress.GetReference()->GetSliceAsset();
- // If this slice has not already been added to the menu then add it.
- if (slicesAddedToMenu.end() == AZStd::find(slicesAddedToMenu.begin(), slicesAddedToMenu.end(), sliceAsset.GetId()))
- {
- const AZStd::string& assetPath = sliceAsset.GetHint();
- slicesAddedToMenu.push_back(sliceAsset.GetId());
- QAction* action = menu->addAction(assetPath.c_str());
- QObject::connect(action, &QAction::triggered, [hierarchy, sliceAsset]
- {
- hierarchy->GetEditorWindow()->EditSliceInNewTab(sliceAsset.GetId());
- }
- );
- }
- }
- }
- }
- }
- }
- }
- else
- {
- if (showMask & Show::kNewSlice)
- {
- QAction* action = addAction(QObject::tr("Make New &Slice from Selection..."));
- QObject::connect(action, &QAction::triggered, hierarchy, [hierarchy]
- {
- hierarchy->GetEditorWindow()->GetSliceManager()->MakeSliceFromSelectedItems(hierarchy, false);
- }
- );
- if (selectedItems.size() == 0)
- {
- action->setEnabled(false);
- }
- }
- }
- #endif
- }
- void HierarchyMenu::New_EmptyElement(HierarchyWidget* hierarchy,
- QTreeWidgetItemRawPtrQList& selectedItems,
- QMenu* menu,
- bool addAtRoot,
- const QPoint* optionalPos)
- {
- menu->addAction(HierarchyHelpers::CreateAddElementAction(hierarchy,
- selectedItems,
- addAtRoot,
- optionalPos));
- }
- void HierarchyMenu::New_ElementFromSlice(HierarchyWidget* hierarchy,
- QTreeWidgetItemRawPtrQList& selectedItems,
- QMenu* menu,
- bool addAtRoot,
- const QPoint* optionalPos)
- {
- #if ENABLE_UI_SLICE_MENU_ITEMS
- AZ::Vector2 viewportPosition(-1.0f,-1.0f); // indicates no viewport position specified
- if (optionalPos)
- {
- // Convert position to render viewport coords
- QPointF scaledPosition = *optionalPos * hierarchy->GetEditorWindow()->GetViewport()->WidgetToViewportFactor();
- viewportPosition = QtHelpers::QPointFToVector2(scaledPosition);
- }
- SliceMenuHelpers::CreateInstantiateSliceMenu(hierarchy,
- selectedItems,
- menu,
- addAtRoot,
- viewportPosition);
- QAction* action = menu->addAction(QObject::tr("Element from Slice &Browser..."));
- QObject::connect(
- action,
- &QAction::triggered,
- hierarchy, [hierarchy, optionalPos]
- {
- AZ::Vector2 viewportPosition(-1.0f,-1.0f); // indicates no viewport position specified
- if (optionalPos)
- {
- // Convert position to render viewport coords
- QPointF scaledPosition = *optionalPos * hierarchy->GetEditorWindow()->GetViewport()->WidgetToViewportFactor();
- viewportPosition = QtHelpers::QPointFToVector2(scaledPosition);
- }
- hierarchy->GetEditorWindow()->GetSliceManager()->InstantiateSliceUsingBrowser(hierarchy, viewportPosition);
- }
- );
- #endif
- }
- void HierarchyMenu::AddComponents(HierarchyWidget* hierarchy,
- QTreeWidgetItemRawPtrQList& selectedItems)
- {
- addActions(ComponentHelpers::CreateAddComponentActions(hierarchy,
- selectedItems,
- this));
- }
- void HierarchyMenu::DeleteElement(HierarchyWidget* hierarchy,
- QTreeWidgetItemRawPtrQList& selectedItems)
- {
- QAction* action;
- // Delete element.
- {
- action = new QAction("Delete", this);
- action->setShortcut(QKeySequence::Delete);
- action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- QObject::connect(action,
- &QAction::triggered,
- hierarchy,
- [ hierarchy ]([[maybe_unused]] bool checked)
- {
- QMetaObject::invokeMethod(hierarchy, "DeleteSelectedItems", Qt::QueuedConnection);
- });
- addAction(action);
- if (selectedItems.empty())
- {
- // Nothing has been selected.
- // We want the menu to be visible, but disabled.
- action->setEnabled(false);
- }
- }
- }
- void HierarchyMenu::FindElements(HierarchyWidget* hierarchy,
- [[maybe_unused]] QTreeWidgetItemRawPtrQList& selectedItems)
- {
- QAction* action;
- // Find elements
- {
- action = new QAction("Find Elements...", this);
- action->setShortcut(QKeySequence::Find);
- action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
- QObject::connect(action,
- &QAction::triggered,
- hierarchy,
- [ hierarchy ]([[maybe_unused]] bool checked)
- {
- hierarchy->GetEditorWindow()->ShowEntitySearchModal();
- });
- addAction(action);
- }
- }
- void HierarchyMenu::EditorOnly(HierarchyWidget* hierarchy,
- QTreeWidgetItemRawPtrQList& selectedItems)
- {
- QAction* action;
- // Toggle editor only state.
- {
- action = new QAction("Editor Only", this);
- action->setCheckable(true);
-
- if (selectedItems.empty())
- {
- action->setChecked(false);
- action->setEnabled(false);
- }
- else
- {
- EntityHelpers::EntityIdList entityIds = SelectionHelpers::GetSelectedElementIds(hierarchy, selectedItems, false);
- bool checked = true;
- for (auto entityId : entityIds)
- {
- bool isEditorOnly = false;
- AzToolsFramework::EditorOnlyEntityComponentRequestBus::EventResult(isEditorOnly, entityId, &AzToolsFramework::EditorOnlyEntityComponentRequests::IsEditorOnlyEntity);
-
- if (!isEditorOnly)
- {
- checked = false;
- break;
- }
- }
- action->setChecked(checked);
- action->setEnabled(true);
- }
- QObject::connect(action,
- &QAction::triggered,
- [hierarchy](bool checked)
- {
- QMetaObject::invokeMethod(hierarchy, "SetEditorOnlyForSelectedItems", Qt::QueuedConnection, Q_ARG(bool, checked));
- });
- addAction(action);
- }
- }
- #include <moc_HierarchyMenu.cpp>
|