Files
o3de/Gems/LyShine/Code/Editor/HierarchyMenu.cpp
T
Esteban Papp 1f9b284de2 Merge branch 'development' into cmake/SPEC-7179
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>

# Conflicts:
#	Code/Editor/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin_precompiled.h
#	Code/Editor/Plugins/EditorCommon/EditorCommon_precompiled.h
#	Code/Editor/Plugins/EditorCommon/stdafx.cpp
#	Code/Editor/Plugins/FFMPEGPlugin/FFMPEGPlugin_precompiled.h
#	Code/Editor/Plugins/PerforcePlugin/PerforcePlugin_precompiled.h
#	Code/Editor/Plugins/ProjectSettingsTool/ProjectSettingsTool_precompiled.h
#	Code/Framework/AzToolsFramework/AzToolsFramework/AzToolsFramework_precompiled.h
#	Code/Tools/AssetProcessor/native/precompiled.h
#	Code/Tools/Standalone/StandaloneTools_precompiled.h
#	Gems/AssetMemoryAnalyzer/Code/Source/AssetMemoryAnalyzer_precompiled.h
#	Gems/Atom/Asset/ImageProcessingAtom/Code/Source/ImageProcessing_precompiled.h
#	Gems/Atom/RHI/DX12/Code/Source/RHI/Atom_RHI_DX12_precompiled.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/Mac/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Include/Platform/iOS/Atom_RHI_Metal_precompiled_Platform.h
#	Gems/Atom/RHI/Metal/Code/Source/Atom_RHI_Metal_precompiled.h
#	Gems/Atom/RHI/Metal/Code/atom_rhi_metal_common_files.cmake
#	Gems/Atom/RHI/Null/Code/Source/Atom_RHI_Null_precompiled.h
#	Gems/Atom/RHI/Null/Code/atom_rhi_null_common_files.cmake
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Android/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Linux/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Mac/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Include/Platform/Windows/Atom_RHI_Vulkan_precompiled_Platform.h
#	Gems/Atom/RHI/Vulkan/Code/Source/Atom_RHI_Vulkan_precompiled.h
#	Gems/Atom/RHI/Vulkan/Code/Source/RHI/SwapChain.cpp
#	Gems/Atom/RHI/Vulkan/Code/atom_rhi_vulkan_common_files.cmake
#	Gems/AtomLyIntegration/AtomFont/Code/Include/AtomLyIntegration/AtomFont/AtomFont_precompiled.h
#	Gems/Blast/Code/Source/StdAfx.cpp
#	Gems/Camera/Code/Source/Camera_precompiled.h
#	Gems/EMotionFX/Code/Source/EMotionFX_precompiled.h
#	Gems/FastNoise/Code/Source/FastNoise_precompiled.h
#	Gems/Gestures/Code/Source/Gestures_precompiled.h
#	Gems/GradientSignal/Code/Source/GradientSignal_precompiled.h
#	Gems/GraphCanvas/Code/precompiled.h
#	Gems/ImGui/Code/Source/ImGui_precompiled.h
#	Gems/InAppPurchases/Code/Source/InAppPurchases_precompiled.h
#	Gems/LmbrCentral/Code/Source/LmbrCentral_precompiled.h
#	Gems/LmbrCentral/Code/Tests/ShapeGeometryUtilTest.cpp
#	Gems/LyShine/Code/Editor/UiCanvasEditor_precompiled.h
#	Gems/LyShine/Code/Source/Animation/LyShine_precompiled.h
#	Gems/LyShine/Code/Source/LyShine_precompiled.h
#	Gems/LyShineExamples/Code/Source/LyShineExamples_precompiled.h
#	Gems/Maestro/Code/Source/Cinematics/Maestro_precompiled.h
#	Gems/Maestro/Code/Source/Maestro_precompiled.h
#	Gems/MessagePopup/Code/Source/MessagePopup_precompiled.h
#	Gems/Metastream/Code/Source/Metastream_precompiled.h
#	Gems/Microphone/Code/Source/Microphone_precompiled.h
#	Gems/Multiplayer/Code/Source/Multiplayer_precompiled.h
#	Gems/PhysX/Code/NumericalMethods/Source/NumericalMethods_precompiled.h
#	Gems/PhysX/Code/Source/PhysXUnsupported_precompiled.h
#	Gems/PhysX/Code/Source/PhysX_precompiled.h
#	Gems/PhysX/Code/physx_unsupported_files.cmake
#	Gems/PhysXDebug/Code/Source/PhysXDebugUnsupported_precompiled.h
#	Gems/PhysXDebug/Code/Source/PhysXDebug_precompiled.h
#	Gems/ScriptCanvas/Code/Editor/precompiled.h
#	Gems/ScriptCanvas/Code/Source/precompiled.h
#	Gems/ScriptCanvasDeveloper/Code/Source/precompiled.h
#	Gems/ScriptCanvasPhysics/Code/Source/ScriptCanvasPhysics_precompiled.h
#	Gems/ScriptEvents/Code/Source/precompiled.h
#	Gems/ScriptEvents/Code/Tests/Editor/EditorTests.cpp
#	Gems/ScriptedEntityTweener/Code/Source/ScriptedEntityTweener_precompiled.h
#	Gems/SliceFavorites/Code/Source/SliceFavorites_precompiled.h
#	Gems/StartingPointCamera/Code/Source/StartingPointCamera_precompiled.h
#	Gems/StartingPointInput/Code/Source/StartingPointInput_precompiled.h
#	Gems/StartingPointMovement/Code/Source/StartingPointMovement_precompiled.h
#	Gems/SurfaceData/Code/Source/SurfaceData_precompiled.h
#	Gems/TextureAtlas/Code/Source/TextureAtlas_precompiled.h
#	Gems/TickBusOrderViewer/Code/Source/TickBusOrderViewer_precompiled.h
#	Gems/Twitch/Code/Source/Twitch_precompiled.h
#	Gems/VirtualGamepad/Code/Source/VirtualGamepad_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBoxUnsupported_precompiled.h
#	Gems/WhiteBox/Code/Source/WhiteBox_precompiled.h
2021-07-16 15:42:37 -07:00

520 lines
18 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 <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, selectedEntities]
{
hierarchy->GetEditorWindow()->GetSliceManager()->MakeSliceFromSelectedItems(hierarchy, true);
}
);
action = addAction(QObject::tr("Make Detached Slice from Selected Entities..."));
QObject::connect(action, &QAction::triggered, hierarchy, [hierarchy, selectedEntities]
{
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, [this, 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)
{
viewportPosition = QtHelpers::QPointFToVector2(*optionalPos);
}
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)
{
viewportPosition = QtHelpers::QPointFToVector2(*optionalPos);
}
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>