123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059 |
- /*
- * 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 <QGraphicsWidget>
- #include <QTimer>
- #include <QSequentialAnimationGroup>
- #include <AzCore/std/containers/unordered_map.h>
- #include <AzCore/Serialization/SerializeContext.h>
- #include <Components/Nodes/Group/CollapsedNodeGroupComponent.h>
- #include <Components/LayerControllerComponent.h>
- #include <Components/Nodes/General/GeneralNodeLayoutComponent.h>
- #include <GraphCanvas/Components/Connections/ConnectionBus.h>
- #include <GraphCanvas/Components/Nodes/Comment/CommentBus.h>
- #include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
- #include <GraphCanvas/Components/Slots/SlotBus.h>
- #include <GraphCanvas/GraphCanvasBus.h>
- #include <GraphCanvas/Editor/GraphModelBus.h>
- #include <GraphCanvas/Utils/ConversionUtils.h>
- namespace GraphCanvas
- {
- //////////////////////////
- // RedirectedSlotWatcher
- //////////////////////////
- RedirectedSlotWatcher::~RedirectedSlotWatcher()
- {
- NodeNotificationBus::MultiHandler::BusDisconnect();
- }
- void RedirectedSlotWatcher::ConfigureWatcher(const AZ::EntityId& collapsedGroupId)
- {
- m_collapsedGroupId = collapsedGroupId;
- NodeNotificationBus::MultiHandler::BusDisconnect();
- }
- void RedirectedSlotWatcher::RegisterEndpoint(const Endpoint& sourceEndpoint, const Endpoint& remappedEndpoint)
- {
- m_endpointMapping[sourceEndpoint] = remappedEndpoint;
- NodeNotificationBus::MultiHandler::BusConnect(sourceEndpoint.GetNodeId());
- }
- void RedirectedSlotWatcher::OnNodeAboutToBeDeleted()
- {
- const AZ::EntityId* nodeRemoved = NodeNotificationBus::GetCurrentBusId();
- if (nodeRemoved)
- {
- auto mapIter = m_endpointMapping.begin();
- while (mapIter != m_endpointMapping.end())
- {
- if (mapIter->first.GetNodeId() == (*nodeRemoved))
- {
- NodeRequestBus::Event(m_collapsedGroupId, &NodeRequests::RemoveSlot, mapIter->second.GetSlotId());
- mapIter = m_endpointMapping.erase(mapIter);
- }
- else
- {
- ++mapIter;
- }
- }
- NodeNotificationBus::MultiHandler::BusDisconnect((*nodeRemoved));
- }
- }
- void RedirectedSlotWatcher::OnSlotRemovedFromNode(const AZ::EntityId& slotId)
- {
- const AZ::EntityId* nodeSource = NodeNotificationBus::GetCurrentBusId();
- if (nodeSource)
- {
- Endpoint sourceEndpoint((*nodeSource), slotId);
- auto endpointIter = m_endpointMapping.find(sourceEndpoint);
- if (endpointIter != m_endpointMapping.end())
- {
- NodeRequestBus::Event(m_collapsedGroupId, &NodeRequests::RemoveSlot, endpointIter->second.GetSlotId());
- m_endpointMapping.erase(endpointIter);
- bool maintainConnection = false;
- for (const auto& mapPair : m_endpointMapping)
- {
- if (mapPair.first.GetNodeId() == (*nodeSource))
- {
- maintainConnection = true;
- break;
- }
- }
- if (!maintainConnection)
- {
- NodeNotificationBus::MultiHandler::BusDisconnect((*nodeSource));
- }
- }
- }
- }
- ////////////////////////////////
- // CollapsedNodeGroupComponent
- ////////////////////////////////
- constexpr int k_collapsingAnimationTimeMS = 175;
- constexpr int k_fadeInTimeMS = 50;
- // 0.9f found through the scientific process of it looking right.
- constexpr float k_endpointanimationTimeSec = (k_collapsingAnimationTimeMS/1000.0f) * 0.9f;
- // General frame delay to ensure that qt has updated and refreshed its display so that everything looks right.
- // 3 is just a magic number found through visual testing.
- constexpr int k_qtFrameDelay = 3;
-
- void CollapsedNodeGroupComponent::Reflect(AZ::ReflectContext* context)
- {
- AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
-
- if (serializeContext)
- {
- serializeContext->Class<SlotRedirectionConfiguration>()
- ->Version(1)
- ->Field("Name", &SlotRedirectionConfiguration::m_name)
- ->Field("TargetId", &SlotRedirectionConfiguration::m_targetEndpoint)
- ;
- serializeContext->Class<CollapsedNodeGroupComponent, GraphCanvasPropertyComponent>()
- ->Version(1)
- ;
- }
- }
-
- AZ::Entity* CollapsedNodeGroupComponent::CreateCollapsedNodeGroupEntity(const CollapsedNodeGroupConfiguration& config)
- {
- AZ::Entity* nodeEntity = GeneralNodeLayoutComponent::CreateGeneralNodeEntity(".collapsedGroup", config);
- nodeEntity->CreateComponent<CollapsedNodeGroupComponent>(config);
- return nodeEntity;
- }
-
- CollapsedNodeGroupComponent::CollapsedNodeGroupComponent()
- : GraphCanvasPropertyComponent()
- , m_animationDelayCounter(0)
- , m_isExpandingOccluderAnimation(false)
- , m_occluderDestructionCounter(0)
- , m_unhideOnAnimationComplete(false)
- , m_deleteObjects(true)
- , m_positionDirty(false)
- , m_ignorePositionChanges(true)
- {
- // Two part animation.
- QSequentialAnimationGroup* opacityGroup = new QSequentialAnimationGroup();
- QPropertyAnimation* delayAnimation = new QPropertyAnimation();
- delayAnimation->setDuration((k_collapsingAnimationTimeMS - k_fadeInTimeMS));
- opacityGroup->addAnimation(delayAnimation);
- m_opacityAnimation = new QPropertyAnimation();
- m_opacityAnimation->setDuration(k_fadeInTimeMS);
- m_opacityAnimation->setPropertyName("opacity");
- m_opacityAnimation->setStartValue(1.0f);
- m_opacityAnimation->setEndValue(0.25f);
- opacityGroup->addAnimation(m_opacityAnimation);
- opacityGroup->setLoopCount(1);
- m_occluderAnimation.addAnimation(opacityGroup);
- m_sizeAnimation = new QPropertyAnimation();
- m_sizeAnimation->setDuration(k_collapsingAnimationTimeMS);
- m_sizeAnimation->setPropertyName("size");
- m_occluderAnimation.addAnimation(m_sizeAnimation);
- m_positionAnimation = new QPropertyAnimation();
- m_positionAnimation->setDuration(k_collapsingAnimationTimeMS);
- m_positionAnimation->setPropertyName("pos");
- m_occluderAnimation.addAnimation(m_positionAnimation);
- QObject::connect(&m_occluderAnimation, &QAnimationGroup::finished, [this]()
- {
- OnAnimationFinished();
- });
- }
-
- CollapsedNodeGroupComponent::CollapsedNodeGroupComponent(const CollapsedNodeGroupConfiguration& config)
- : CollapsedNodeGroupComponent()
- {
- m_nodeGroupId = config.m_nodeGroupId;
- }
-
- CollapsedNodeGroupComponent::~CollapsedNodeGroupComponent()
- {
- }
-
- void CollapsedNodeGroupComponent::Init()
- {
- GraphCanvasPropertyComponent::Init();
- m_memberHiddenStateSetter.AddStateController(&m_ignorePositionChanges);
- m_memberDraggedStateSetter.AddStateController(&m_ignorePositionChanges);
- }
-
- void CollapsedNodeGroupComponent::Activate()
- {
- GraphCanvasPropertyComponent::Activate();
- AZ::EntityId entityId = GetEntityId();
- m_redirectedSlotWatcher.ConfigureWatcher(entityId);
-
- CollapsedNodeGroupRequestBus::Handler::BusConnect(entityId);
- VisualNotificationBus::Handler::BusConnect(entityId);
- NodeNotificationBus::Handler::BusConnect(entityId);
- SceneMemberNotificationBus::Handler::BusConnect(entityId);
- GeometryNotificationBus::Handler::BusConnect(entityId);
- }
-
- void CollapsedNodeGroupComponent::Deactivate()
- {
- GraphCanvasPropertyComponent::Deactivate();
- GroupableSceneMemberNotificationBus::Handler::BusDisconnect();
- CommentNotificationBus::Handler::BusDisconnect();
- GeometryNotificationBus::Handler::BusDisconnect();
- SceneMemberNotificationBus::Handler::BusDisconnect();
- NodeNotificationBus::Handler::BusDisconnect();
- VisualNotificationBus::Handler::BusDisconnect();
- CollapsedNodeGroupRequestBus::Handler::BusDisconnect();
- AZ::SystemTickBus::Handler::BusDisconnect();
- }
- void CollapsedNodeGroupComponent::OnSystemTick()
- {
- // Delay count for Qt to catch up with the visuals so I can animate in a visually pleasing way.
- if (m_animationDelayCounter > 0)
- {
- m_animationDelayCounter--;
-
- if (m_animationDelayCounter <= 0)
- {
- AnimateOccluder(m_isExpandingOccluderAnimation);
- m_isExpandingOccluderAnimation = false;
- m_animationDelayCounter = 0;
- UpdateSystemTickBus();
- }
- }
- if (m_occluderDestructionCounter > 0)
- {
- m_occluderDestructionCounter--;
- if (m_occluderDestructionCounter <= 0)
- {
- AZ::EntityId graphId;
- SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
- if (m_effectId.IsValid())
- {
- SceneRequestBus::Event(graphId, &SceneRequests::CancelGraphicsEffect, m_effectId);
- m_effectId.SetInvalid();
- }
- m_occluderDestructionCounter = 0;
- UpdateSystemTickBus();
- CollapsedNodeGroupNotificationBus::Event(GetEntityId(), &CollapsedNodeGroupNotifications::OnExpansionComplete);
- AZStd::unordered_set<NodeId> deleteIds = { GetEntityId() };
- SceneRequestBus::Event(graphId, &SceneRequests::Delete, deleteIds);
- }
- }
- }
-
- void CollapsedNodeGroupComponent::OnAddedToScene(const GraphId& graphId)
- {
- SceneNotificationBus::Handler::BusConnect(graphId);
- m_containedSubGraphs.Clear();
- AZStd::string comment;
- CommentRequestBus::EventResult(comment, m_nodeGroupId, &CommentRequests::GetComment);
- NodeTitleRequestBus::Event(GetEntityId(), &NodeTitleRequests::SetTitle, comment);
- NodeTitleRequestBus::Event(GetEntityId(), &NodeTitleRequests::SetSubTitle, "Collapsed Node Group");
- AZ::Color color;
- NodeGroupRequestBus::EventResult(color, m_nodeGroupId, &NodeGroupRequests::GetGroupColor);
- OnBackgroundColorChanged(color);
- AZStd::vector< NodeId > groupedElements;
- NodeGroupRequestBus::Event(m_nodeGroupId, &NodeGroupRequests::FindGroupedElements, groupedElements);
- AZStd::vector< NodeId > elementsToManage;
- elementsToManage.reserve(groupedElements.size());
- AZStd::vector< NodeId > elementsToSearch = groupedElements;
- while (!elementsToSearch.empty())
- {
- AZ::EntityId searchedElement = elementsToSearch.front();
- elementsToSearch.erase(elementsToSearch.begin());
- if (GraphUtils::IsNodeGroup(searchedElement))
- {
- QGraphicsItem* graphicsItem = nullptr;
- SceneMemberUIRequestBus::EventResult(graphicsItem, searchedElement, &SceneMemberUIRequests::GetRootGraphicsItem);
- if (graphicsItem->isVisible())
- {
- elementsToManage.emplace_back(searchedElement);
- AZStd::vector<NodeId> subGroupedElements;
- NodeGroupRequestBus::Event(searchedElement, &NodeGroupRequests::FindGroupedElements, subGroupedElements);
- if (!subGroupedElements.empty())
- {
- elementsToSearch.insert(elementsToSearch.end(), subGroupedElements.begin(), subGroupedElements.end());
- elementsToManage.reserve(elementsToManage.size() + subGroupedElements.size());
- }
- }
- }
- else
- {
- elementsToManage.emplace_back(searchedElement);
- }
- }
- SubGraphParsingConfig config;
- config.m_ignoredGraphMembers.insert(GetEntityId());
- config.m_createNonConnectableSubGraph = true;
- m_containedSubGraphs = GraphUtils::ParseSceneMembersIntoSubGraphs(elementsToManage, config);
-
- ConstructGrouping(graphId);
- SetupGroupPosition(graphId);
- CommentNotificationBus::Handler::BusConnect(m_nodeGroupId);
- bool isLoading = false;
- SceneRequestBus::EventResult(isLoading, graphId, &SceneRequests::IsLoading);
- bool isPasting = false;
- SceneRequestBus::EventResult(isPasting, graphId, &SceneRequests::IsPasting);
- GroupableSceneMemberNotificationBus::Handler::BusConnect(GetEntityId());
- if (!isLoading && !isPasting)
- {
- CreateOccluder(graphId, m_nodeGroupId);
- // Node won't be the correct size right away, need to wait for Qt to tick an update.
- TriggerCollapseAnimation();
- }
- }
-
- void CollapsedNodeGroupComponent::OnRemovedFromScene(const GraphId& graphId)
- {
- if (m_effectId.IsValid())
- {
- SceneRequestBus::Event(graphId, &SceneRequests::CancelGraphicsEffect, m_effectId);
- m_effectId.SetInvalid();
- }
- if (m_deleteObjects)
- {
- GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPushPreventUndoStateUpdate);
- SceneRequestBus::Event(graphId, &SceneRequests::Delete, m_containedSubGraphs.m_nonConnectableGraph.m_containedNodes);
- for (const GraphSubGraph& subGraph : m_containedSubGraphs.m_subGraphs)
- {
- SceneRequestBus::Event(graphId, &SceneRequests::Delete, subGraph.m_containedNodes);
- }
- AZStd::unordered_set< AZ::EntityId > deletionIds = { m_nodeGroupId };
- SceneRequestBus::Event(graphId, &SceneRequests::Delete, deletionIds);
- GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPopPreventUndoStateUpdate);
- }
- SceneNotificationBus::Handler::BusDisconnect();
- AZ::SystemTickBus::Handler::BusDisconnect();
- }
- void CollapsedNodeGroupComponent::OnBoundsChanged()
- {
- if (AZ::SystemTickBus::Handler::BusIsConnected())
- {
- AZ::EntityId graphId;
- SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
- SetupGroupPosition(graphId);
- if (m_animationDelayCounter != 0)
- {
- m_animationDelayCounter = k_qtFrameDelay;
- }
- }
- }
- void CollapsedNodeGroupComponent::OnPositionChanged(const AZ::EntityId& /*targetEntity*/, const AZ::Vector2& position)
- {
- if (!m_ignorePositionChanges.GetState())
- {
- MoveGroupedElementsBy(position - m_previousPosition);
- m_previousPosition = position;
- }
- else
- {
- m_positionDirty = true;
- }
- }
- void CollapsedNodeGroupComponent::OnSceneMemberDragBegin()
- {
- m_memberDraggedStateSetter.SetState(true);
- }
- void CollapsedNodeGroupComponent::OnSceneMemberDragComplete()
- {
- m_memberDraggedStateSetter.ReleaseState();
- // This is a quick implementation of this. Really this shouldn't be necessary as I can just
- // calculate the offset when the group is broken and just apply the changes then.
- //
- // But for simplicity now, I'll do this the quick way and just update everything after each move.
- if (m_positionDirty)
- {
- m_positionDirty = false;
- AZ::Vector2 position;
- GeometryRequestBus::EventResult(position, GetEntityId(), &GeometryRequests::GetPosition);
- MoveGroupedElementsBy(position - m_previousPosition);
- m_previousPosition = position;
- }
- }
- void CollapsedNodeGroupComponent::OnCommentChanged(const AZStd::string& comment)
- {
- NodeTitleRequestBus::Event(GetEntityId(), &NodeTitleRequests::SetTitle, comment);
- NodeUIRequestBus::Event(GetEntityId(), &NodeUIRequests::AdjustSize);
- }
- void CollapsedNodeGroupComponent::OnBackgroundColorChanged(const AZ::Color& color)
- {
- QColor titleColor = ConversionUtils::AZToQColor(color);
- NodeTitleRequestBus::Event(GetEntityId(), &NodeTitleRequests::SetColorPaletteOverride, titleColor);
- }
- bool CollapsedNodeGroupComponent::OnMouseDoubleClick(const QGraphicsSceneMouseEvent* /*mouseEvent*/)
- {
- ExpandGroup();
- return true;
- }
- void CollapsedNodeGroupComponent::ExpandGroup()
- {
- GraphId graphId;
- SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
- ReverseGrouping(graphId);
- }
- AZ::EntityId CollapsedNodeGroupComponent::GetSourceGroup() const
- {
- return m_nodeGroupId;
- }
- AZStd::vector< Endpoint > CollapsedNodeGroupComponent::GetRedirectedEndpoints() const
- {
- AZStd::vector< Endpoint > redirectedEndpoints;
- for (const auto& redirectionConfigurations : m_redirections)
- {
- AZStd::unordered_set< Endpoint > remappedEndpoints = GraphUtils::RemapEndpointForModel(redirectionConfigurations.m_targetEndpoint);
- redirectedEndpoints.insert(redirectedEndpoints.begin(), remappedEndpoints.begin(), remappedEndpoints.end());
- }
- return redirectedEndpoints;
- }
- void CollapsedNodeGroupComponent::ForceEndpointRedirection(const AZStd::vector<Endpoint>& endpoints)
- {
- m_forcedRedirections.insert(endpoints.begin(), endpoints.end());
- }
- void CollapsedNodeGroupComponent::OnGroupChanged()
- {
- AZ::EntityId groupId;
- GroupableSceneMemberRequestBus::EventResult(groupId, GetEntityId(), &GroupableSceneMemberRequests::GetGroupId);
- if (groupId.IsValid())
- {
- NodeGroupRequestBus::Event(groupId, &NodeGroupRequests::AddElementToGroup, m_nodeGroupId);
- }
- else
- {
- GroupableSceneMemberRequestBus::Event(m_nodeGroupId, &GroupableSceneMemberRequests::RemoveFromGroup);
- }
- }
- void CollapsedNodeGroupComponent::OnSceneMemberHidden()
- {
- m_memberHiddenStateSetter.SetState(true);
- }
- void CollapsedNodeGroupComponent::OnSceneMemberShown()
- {
- m_memberHiddenStateSetter.ReleaseState();
- }
- void CollapsedNodeGroupComponent::SetupGroupPosition(const GraphId& /*graphId*/)
- {
- StateSetter<bool> ignorePositionSetter;
- ignorePositionSetter.AddStateController(&m_ignorePositionChanges);
- ignorePositionSetter.SetState(true);
- QPointF centerPoint;
- QGraphicsItem* blockItem = nullptr;
- SceneMemberUIRequestBus::EventResult(blockItem, m_nodeGroupId, &SceneMemberUIRequests::GetRootGraphicsItem);
- if (blockItem)
- {
- centerPoint = blockItem->sceneBoundingRect().center();
- }
- // Want to adjust the position of the node to make it a little more centered.
- // But the scene component will re-position it to the passed in location
- // before it attempts this part(and the node needs a frame to adjust it's sizing to be correct)
- //
- // So, fire off a single shot timer and hope we never create/delete this thing.
- QGraphicsItem* graphicsItem = nullptr;
- SceneMemberUIRequestBus::EventResult(graphicsItem, GetEntityId(), &SceneMemberUIRequests::GetRootGraphicsItem);
- QRectF boundingRect = graphicsItem->sceneBoundingRect();
- qreal width = boundingRect.width();
- qreal height = boundingRect.height();
- // Want the Collapsed Node Group to appear centered over top of the Node Group.
- AZ::Vector2 offset(aznumeric_cast<float>(width * 0.5f), aznumeric_cast<float>(height * 0.5f));
- m_previousPosition = ConversionUtils::QPointToVector(centerPoint);
- m_previousPosition -= offset;
- graphicsItem->setPos(ConversionUtils::AZToQPoint(m_previousPosition));
- GeometryRequestBus::Event(GetEntityId(), &GeometryRequests::SetPosition, m_previousPosition);
- // Reget the position we set since we might have snapped to a grid.
- GeometryRequestBus::EventResult(m_previousPosition, GetEntityId(), &GeometryRequests::GetPosition);
-
- m_positionDirty = false;
- }
- void CollapsedNodeGroupComponent::CreateOccluder(const GraphId& graphId, const AZ::EntityId& initialElement)
- {
- if (m_effectId.IsValid())
- {
- GraphCanvas::SceneRequestBus::Event(graphId, &GraphCanvas::SceneRequests::CancelGraphicsEffect, m_effectId);
- }
- QGraphicsItem* graphicsItem = nullptr;
- VisualRequestBus::EventResult(graphicsItem, initialElement, &VisualRequests::AsGraphicsItem);
- if (graphicsItem == nullptr)
- {
- return;
- }
- AZ::Color groupColor;
- NodeGroupRequestBus::EventResult(groupColor, m_nodeGroupId, &NodeGroupRequests::GetGroupColor);
- OccluderConfiguration configuration;
- configuration.m_renderColor = ConversionUtils::AZToQColor(groupColor);
- configuration.m_bounds = graphicsItem->sceneBoundingRect();
- configuration.m_zValue = LayerUtils::AlwaysOnTopZValue();
- SceneRequestBus::EventResult(m_effectId, graphId, &SceneRequests::CreateOccluder, configuration);
- }
- void CollapsedNodeGroupComponent::AnimateOccluder(bool isExpanding)
- {
- m_unhideOnAnimationComplete = isExpanding;
- AZ::EntityId graphId;
- SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
- if (!m_effectId.IsValid())
- {
- if (isExpanding)
- {
- CreateOccluder(graphId, GetEntityId());
- }
- else
- {
- CreateOccluder(graphId, m_nodeGroupId);
- }
- }
- if (!m_effectId.IsValid())
- {
- OnAnimationFinished();
- }
- QGraphicsItem* blockItem = nullptr;
- VisualRequestBus::EventResult(blockItem, m_nodeGroupId, &VisualRequests::AsGraphicsItem);
- if (blockItem == nullptr)
- {
- OnAnimationFinished();
- return;
- }
- QGraphicsItem* graphicsItem = nullptr;
- SceneMemberUIRequestBus::EventResult(graphicsItem, GetEntityId(), &SceneMemberUIRequests::GetRootGraphicsItem);
- if (graphicsItem == nullptr)
- {
- OnAnimationFinished();
- return;
- }
- QGraphicsItem* occluderItem = nullptr;
- GraphicsEffectRequestBus::EventResult(occluderItem, m_effectId, &GraphicsEffectRequests::AsQGraphicsItem);
- if (occluderItem)
- {
- QRectF startRect = occluderItem->sceneBoundingRect();
- QRectF targetRect;
- if (isExpanding)
- {
- targetRect = blockItem->sceneBoundingRect();
- }
- else
- {
- targetRect = graphicsItem->sceneBoundingRect();
- }
- QGraphicsObject* occluderObject = static_cast<QGraphicsObject*>(occluderItem);
- m_sizeAnimation->setTargetObject(occluderObject);
- m_sizeAnimation->setStartValue(startRect.size());
- m_sizeAnimation->setEndValue(targetRect.size());
- m_positionAnimation->setTargetObject(occluderObject);
- m_positionAnimation->setStartValue(startRect.topLeft());
- m_positionAnimation->setEndValue(targetRect.topLeft());
- m_opacityAnimation->setTargetObject(occluderObject);
- m_occluderAnimation.start();
- }
- }
- void CollapsedNodeGroupComponent::ConstructGrouping(const GraphId& graphId)
- {
- bool isLoading = false;
- SceneRequestBus::EventResult(isLoading, graphId, &SceneRequests::IsLoading);
- bool isPasting = false;
- SceneRequestBus::EventResult(isPasting, graphId, &SceneRequests::IsPasting);
- // Keeps track of a mapping from the raw slot Id to the corresponding slot endpoint that we created.
- AZStd::unordered_map< SlotId, SlotId > internalSlotMappings;
- OrderedEndpointSet sourceEndpointOrdering;
- AZStd::unordered_multimap< Endpoint, ConnectionId > sourceEndpointRemapping;
- OrderedEndpointSet targetEndpointOrdering;
- AZStd::unordered_multimap< Endpoint, ConnectionId > targetEndpointRemapping;
- for (const NodeId& nodeId : m_containedSubGraphs.m_nonConnectableGraph.m_containedNodes)
- {
- SceneRequestBus::Event(graphId, &SceneRequests::Hide, nodeId);
- }
- for (const Endpoint& forcedEndpoint : m_forcedRedirections)
- {
- ConnectionType connectionType = ConnectionType::CT_Invalid;
- SlotRequestBus::EventResult(connectionType, forcedEndpoint.GetSlotId(), &SlotRequests::GetConnectionType);
- if (connectionType == ConnectionType::CT_Input)
- {
- targetEndpointOrdering.insert(EndpointOrderingStruct::ConstructOrderingInformation(forcedEndpoint));
- }
- else if (connectionType == ConnectionType::CT_Output)
- {
- sourceEndpointOrdering.insert(EndpointOrderingStruct::ConstructOrderingInformation(forcedEndpoint));
- }
- }
- for (const GraphSubGraph& subGraph : m_containedSubGraphs.m_subGraphs)
- {
- for (const ConnectionId& connectionId : subGraph.m_entryConnections)
- {
- Endpoint targetEndpoint;
- ConnectionRequestBus::EventResult(targetEndpoint, connectionId, &ConnectionRequests::GetTargetEndpoint);
- targetEndpointOrdering.insert(EndpointOrderingStruct::ConstructOrderingInformation(targetEndpoint));
- targetEndpointRemapping.insert(AZStd::make_pair(targetEndpoint, connectionId));
- }
- for (const ConnectionId& connectionId : subGraph.m_exitConnections)
- {
- Endpoint sourceEndpoint;
- ConnectionRequestBus::EventResult(sourceEndpoint, connectionId, &ConnectionRequests::GetSourceEndpoint);
- sourceEndpointOrdering.insert(EndpointOrderingStruct::ConstructOrderingInformation(sourceEndpoint));
- sourceEndpointRemapping.insert(AZStd::make_pair(sourceEndpoint, connectionId));
- }
- for (const NodeId& nodeId : subGraph.m_containedNodes)
- {
- SceneRequestBus::Event(graphId, &SceneRequests::Hide, nodeId);
- }
- for (const ConnectionId& connectionId : subGraph.m_innerConnections)
- {
- SceneRequestBus::Event(graphId, &SceneRequests::Hide, connectionId);
- }
- }
- for (const EndpointOrderingStruct& targetEndpointStruct : targetEndpointOrdering)
- {
- auto slotIter = internalSlotMappings.find(targetEndpointStruct.m_endpoint.GetSlotId());
- if (slotIter == internalSlotMappings.end())
- {
- SlotId redirectionSlotId = CreateSlotRedirection(graphId, targetEndpointStruct.m_endpoint);
- auto insertIter = internalSlotMappings.insert(AZStd::make_pair(targetEndpointStruct.m_endpoint.GetSlotId(), redirectionSlotId));
- if (insertIter.second)
- {
- SlotRequestBus::Event(redirectionSlotId, &SlotRequests::RemapSlotForModel, targetEndpointStruct.m_endpoint);
- slotIter = insertIter.first;
- }
- }
- if (slotIter == internalSlotMappings.end())
- {
- continue;
- }
- Endpoint mappedTargetEndpoint = Endpoint(GetEntityId(), slotIter->second);
- auto iterPair = targetEndpointRemapping.equal_range(targetEndpointStruct.m_endpoint);
- for (auto mapIter = iterPair.first; mapIter != iterPair.second; ++mapIter)
- {
- if (isLoading || isPasting)
- {
- ConnectionRequestBus::Event(mapIter->second, &ConnectionRequests::SnapTargetDisplayTo, mappedTargetEndpoint);
- }
- else
- {
- ConnectionRequestBus::Event(mapIter->second, &ConnectionRequests::AnimateTargetDisplayTo, mappedTargetEndpoint, k_endpointanimationTimeSec);
- }
- }
- }
- for (const EndpointOrderingStruct& sourceEndpointStruct : sourceEndpointOrdering)
- {
- auto slotIter = internalSlotMappings.find(sourceEndpointStruct.m_endpoint.GetSlotId());
- if (slotIter == internalSlotMappings.end())
- {
- SlotId redirectionSlotId = CreateSlotRedirection(graphId, sourceEndpointStruct.m_endpoint);
- auto insertIter = internalSlotMappings.insert(AZStd::make_pair(sourceEndpointStruct.m_endpoint.GetSlotId(), redirectionSlotId));
- if (insertIter.second)
- {
- SlotRequestBus::Event(redirectionSlotId, &SlotRequests::RemapSlotForModel, sourceEndpointStruct.m_endpoint);
- slotIter = insertIter.first;
- }
- }
- if (slotIter == internalSlotMappings.end())
- {
- continue;
- }
- Endpoint mappedSourceEndpoint = Endpoint(GetEntityId(), slotIter->second);
- auto iterPair = sourceEndpointRemapping.equal_range(sourceEndpointStruct.m_endpoint);
- for (auto mapIter = iterPair.first; mapIter != iterPair.second; ++mapIter)
- {
- if (isLoading || isPasting)
- {
- ConnectionRequestBus::Event(mapIter->second, &ConnectionRequests::SnapSourceDisplayTo, mappedSourceEndpoint);
- }
- else
- {
- ConnectionRequestBus::Event(mapIter->second, &ConnectionRequests::AnimateSourceDisplayTo, mappedSourceEndpoint, k_endpointanimationTimeSec);
- }
- }
- }
- SceneRequestBus::Event(graphId, &SceneRequests::Hide, m_nodeGroupId);
- }
- void CollapsedNodeGroupComponent::ReverseGrouping(const GraphId& graphId)
- {
- AZStd::vector< SlotId > slotIds;
- NodeRequestBus::EventResult(slotIds, GetEntityId(), &NodeRequests::GetSlotIds);
- for (const SlotId& slotId : slotIds)
- {
- ConnectionType connectionType = CT_Invalid;
- SlotRequestBus::EventResult(connectionType, slotId, &SlotRequests::GetConnectionType);
- if (connectionType != CT_Invalid && connectionType != CT_None)
- {
- AZStd::vector< Endpoint > redirectedEndpoints;
- SlotRequestBus::EventResult(redirectedEndpoints, slotId, &SlotRequests::GetRemappedModelEndpoints);
- AZ_Assert(redirectedEndpoints.size() == 1, "A single slot being redirected to multiple slots is not currently supported.");
- if (redirectedEndpoints.size() == 0)
- {
- continue;
- }
- Endpoint redirectedEndpoint = redirectedEndpoints.front();
- AZStd::vector< ConnectionId > connectionIds;
- SlotRequestBus::EventResult(connectionIds, slotId, &SlotRequests::GetConnections);
- for (const ConnectionId& connectionId : connectionIds)
- {
- if (connectionType == CT_Input)
- {
- ConnectionRequestBus::Event(connectionId, &ConnectionRequests::AnimateTargetDisplayTo, redirectedEndpoint, k_endpointanimationTimeSec);
- }
- else if (connectionType == CT_Output)
- {
- ConnectionRequestBus::Event(connectionId, &ConnectionRequests::AnimateSourceDisplayTo, redirectedEndpoint, k_endpointanimationTimeSec);
- }
- }
- }
- }
- CreateOccluder(graphId, GetEntityId());
- TriggerExpandAnimation();
- SceneRequestBus::Event(graphId, &SceneRequests::Hide, GetEntityId());
- }
- void CollapsedNodeGroupComponent::TriggerExpandAnimation()
- {
- m_animationDelayCounter = k_qtFrameDelay;
- m_isExpandingOccluderAnimation = true;
- VisualRequestBus::Event(GetEntityId(), &VisualRequests::SetVisible, false);
- UpdateSystemTickBus();
- }
- void CollapsedNodeGroupComponent::TriggerCollapseAnimation()
- {
- m_animationDelayCounter = k_qtFrameDelay;
- m_isExpandingOccluderAnimation = false;
- VisualRequestBus::Event(GetEntityId(), &VisualRequests::SetVisible, false);
- UpdateSystemTickBus();
- }
- void CollapsedNodeGroupComponent::MoveGroupedElementsBy(const AZ::Vector2& offset)
- {
- GraphId graphId;
- SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
- GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPushPreventUndoStateUpdate);
- // Update the NodeGroup
- {
- AZ::Vector2 position;
- GeometryRequestBus::EventResult(position, m_nodeGroupId, &GeometryRequests::GetPosition);
- position += offset;
- // TODO: Potentially fix the collapsed node groups
- GeometryRequestBus::Event(m_nodeGroupId, &GeometryRequests::SetPosition, position);
- }
- GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPopPreventUndoStateUpdate);
- }
- void CollapsedNodeGroupComponent::MoveSubGraphBy(const GraphSubGraph& subGraph, const AZ::Vector2& offset)
- {
- for (const NodeId& nodeId : subGraph.m_containedNodes)
- {
- AZ::Vector2 position;
- GeometryRequestBus::EventResult(position, nodeId, &GeometryRequests::GetPosition);
- position += offset;
- GeometryRequestBus::Event(nodeId, &GeometryRequests::SetPosition, position);
- }
- }
- void CollapsedNodeGroupComponent::OnAnimationFinished()
- {
- GraphId graphId;
- SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
- if (m_unhideOnAnimationComplete)
- {
- {
- ScopedGraphUndoBlocker undoBlocker(graphId);
- SceneRequestBus::Event(graphId, &SceneRequests::Show, m_nodeGroupId);
- for (const NodeId& nodeId : m_containedSubGraphs.m_nonConnectableGraph.m_containedNodes)
- {
- SceneRequestBus::Event(graphId, &SceneRequests::Show, nodeId);
- }
- for (const GraphSubGraph& subGraph : m_containedSubGraphs.m_subGraphs)
- {
- for (const NodeId& nodeId : subGraph.m_containedNodes)
- {
- SceneRequestBus::Event(graphId, &SceneRequests::Show, nodeId);
- }
- for (const ConnectionId& connectionId : subGraph.m_innerConnections)
- {
- SceneRequestBus::Event(graphId, &SceneRequests::Show, connectionId);
- }
- }
- AZ::EntityId groupId;
- GroupableSceneMemberRequestBus::EventResult(groupId, m_nodeGroupId, &GroupableSceneMemberRequests::GetGroupId);
- if (groupId.IsValid())
- {
- const bool growOnly = true;
- NodeGroupRequestBus::Event(groupId, &NodeGroupRequests::ResizeGroupToElements, growOnly);
- }
- m_deleteObjects = false;
- GroupableSceneMemberNotificationBus::Handler::BusDisconnect();
- // Want to delay removing the occluder, because the wrapper nodes sometime deform slightly and need a tick to visually
- // update.
- m_occluderDestructionCounter = k_qtFrameDelay;
- UpdateSystemTickBus();
- }
- GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestUndoPoint);
- }
- else
- {
- if (m_effectId.IsValid())
- {
- SceneRequestBus::Event(graphId, &SceneRequests::CancelGraphicsEffect, m_effectId);
- m_effectId.SetInvalid();
- }
- VisualRequestBus::Event(GetEntityId(), &VisualRequests::SetVisible, true);
- SceneMemberUIRequestBus::Event(GetEntityId(), &SceneMemberUIRequests::SetSelected, true);
- GraphUtils::SanityCheckEnabledState(GetEntityId());
- }
- }
- SlotId CollapsedNodeGroupComponent::CreateSlotRedirection(const GraphId& /*graphId*/, const Endpoint& endpoint)
- {
- m_redirections.emplace_back();
- SlotRedirectionConfiguration& configuration = m_redirections.back();
- configuration.m_targetEndpoint = endpoint;
- return InitializeRedirectionSlot(configuration);
- }
- SlotId CollapsedNodeGroupComponent::InitializeRedirectionSlot(const SlotRedirectionConfiguration& configuration)
- {
- SlotId retVal;
- SlotConfiguration* cloneConfiguration = nullptr;
- SlotRequestBus::EventResult(cloneConfiguration, configuration.m_targetEndpoint.GetSlotId(), &SlotRequests::CloneSlotConfiguration);
- if (cloneConfiguration)
- {
- if (!configuration.m_name.empty())
- {
- cloneConfiguration->m_name = configuration.m_name;
- }
- else
- {
- AZStd::string nodeTitle;
- NodeTitleRequestBus::EventResult(nodeTitle, configuration.m_targetEndpoint.GetNodeId(), &NodeTitleRequests::GetTitle);
- AZStd::string displayName = AZStd::string::format("%s:%s", nodeTitle.c_str(), cloneConfiguration->m_name.c_str());
- cloneConfiguration->m_name = displayName;
- }
- AZ::Entity* slotEntity = nullptr;
- GraphCanvasRequestBus::BroadcastResult(slotEntity, &GraphCanvasRequests::CreateSlot, GetEntityId(), (*cloneConfiguration));
- if (slotEntity)
- {
- slotEntity->Init();
- slotEntity->Activate();
- GraphCanvas::NodeRequestBus::Event(GetEntityId(), &GraphCanvas::NodeRequests::AddSlot, slotEntity->GetId());
- retVal = slotEntity->GetId();
- }
- delete cloneConfiguration;
- }
- Endpoint redirectedSlot(GetEntityId(), retVal);
- if (redirectedSlot.IsValid())
- {
- m_redirectedSlotWatcher.RegisterEndpoint(configuration.m_targetEndpoint, redirectedSlot);
- }
- return retVal;
- }
- void CollapsedNodeGroupComponent::UpdateSystemTickBus()
- {
- if (m_animationDelayCounter > 0 || m_occluderDestructionCounter > 0)
- {
- AZ::SystemTickBus::Handler::BusConnect();
- }
- else
- {
- AZ::SystemTickBus::Handler::BusDisconnect();
- }
- }
- }
|