Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,159 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
AZ_PUSH_DISABLE_WARNING(4251 4800 4244, "-Wunknown-warning-option")
#include <QPainter>
#include <QStyleOption>
AZ_POP_DISABLE_WARNING
#include <Components/Nodes/General/GeneralNodeFrameComponent.h>
#include <GraphCanvas/Components/GeometryBus.h>
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/Editor/GraphCanvasProfiler.h>
#include <GraphCanvas/tools.h>
#include <GraphCanvas/Styling/StyleHelper.h>
namespace GraphCanvas
{
//////////////////////////////
// GeneralNodeFrameComponent
//////////////////////////////
void GeneralNodeFrameComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<GeneralNodeFrameComponent, AZ::Component>()
->Version(1)
;
}
}
GeneralNodeFrameComponent::GeneralNodeFrameComponent()
: m_frameWidget(nullptr)
, m_shouldDeleteFrame(true)
{
}
GeneralNodeFrameComponent::~GeneralNodeFrameComponent()
{
if (m_shouldDeleteFrame)
{
delete m_frameWidget;
}
}
void GeneralNodeFrameComponent::Init()
{
m_frameWidget = aznew GeneralNodeFrameGraphicsWidget(GetEntityId());
}
void GeneralNodeFrameComponent::Activate()
{
NodeNotificationBus::Handler::BusConnect(GetEntityId());
m_frameWidget->Activate();
}
void GeneralNodeFrameComponent::Deactivate()
{
m_frameWidget->Deactivate();
NodeNotificationBus::Handler::BusDisconnect();
}
void GeneralNodeFrameComponent::OnNodeActivated()
{
QGraphicsLayout* layout = nullptr;
NodeLayoutRequestBus::EventResult(layout, GetEntityId(), &NodeLayoutRequests::GetLayout);
m_frameWidget->setLayout(layout);
}
void GeneralNodeFrameComponent::OnNodeWrapped([[maybe_unused]] const AZ::EntityId& wrappingNode)
{
// When wrapped, our NodeFrame widget is part of another objects layout, and will
// be deleted when that object gets deleted.
m_shouldDeleteFrame = false;
}
void GeneralNodeFrameComponent::OnNodeUnwrapped([[maybe_unused]] const AZ::EntityId& wrappingNode)
{
m_shouldDeleteFrame = true;
}
///////////////////////////////////
// GeneralNodeFrameGraphicsWidget
///////////////////////////////////
GeneralNodeFrameGraphicsWidget::GeneralNodeFrameGraphicsWidget(const AZ::EntityId& entityKey)
: NodeFrameGraphicsWidget(entityKey)
{
}
QPainterPath GeneralNodeFrameGraphicsWidget::GetOutline() const
{
QPainterPath path;
QPen border = m_style.GetBorder();
qreal cornerRadius = GetCornerRadius();
qreal halfBorder = border.widthF() / 2.;
QRectF adjusted = sceneBoundingRect().marginsRemoved(QMarginsF(halfBorder, halfBorder, halfBorder, halfBorder));
if (cornerRadius >= 1.0)
{
path.addRoundedRect(adjusted, cornerRadius, cornerRadius);
}
else
{
path.addRect(adjusted);
}
return path;
}
void GeneralNodeFrameGraphicsWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
QPen border = m_style.GetBorder();
QBrush background = m_style.GetBrush(Styling::Attribute::BackgroundColor);
if (border.style() != Qt::NoPen || background.color().alpha() > 0)
{
qreal cornerRadius = GetCornerRadius();
border.setJoinStyle(Qt::PenJoinStyle::MiterJoin); // sharp corners
painter->setPen(border);
painter->setBrush(background);
qreal halfBorder = border.widthF() / 2.;
QRectF adjusted = boundingRect().marginsRemoved(QMarginsF(halfBorder, halfBorder, halfBorder, halfBorder));
if (cornerRadius >= 1.0)
{
painter->drawRoundedRect(adjusted, cornerRadius, cornerRadius);
}
else
{
painter->drawRect(adjusted);
}
}
QGraphicsWidget::paint(painter, option, widget);
}
}
@@ -0,0 +1,109 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
AZ_PUSH_DISABLE_WARNING(4251 4800 4244, "-Wunknown-warning-option")
#include <QGraphicsWidget>
AZ_POP_DISABLE_WARNING
#include <Components/Nodes/NodeFrameGraphicsWidget.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/Nodes/NodeLayoutBus.h>
#include <GraphCanvas/Components/Nodes/NodeUIBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/Styling/StyleHelper.h>
namespace GraphCanvas
{
class GeneralNodeFrameGraphicsWidget;
class GeneralNodeFrameComponent
: public AZ::Component
, public NodeNotificationBus::Handler
{
public:
AZ_COMPONENT(GeneralNodeFrameComponent, "{3AD0423E-F3D5-45F7-8656-C66BCD1EC691}", AZ::Component);
static void Reflect(AZ::ReflectContext*);
GeneralNodeFrameComponent();
~GeneralNodeFrameComponent() override;
// AZ::Component
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("GraphCanvas_NodeVisualService", 0x39c4e7f3));
provided.push_back(AZ_CRC("GraphCanvas_RootVisualService", 0x9ec46d3b));
provided.push_back(AZ_CRC("GraphCanvas_VisualService", 0xfbb2c871));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("GraphCanvas_NodeVisualService", 0x39c4e7f3));
incompatible.push_back(AZ_CRC("GraphCanvas_RootVisualService", 0x9ec46d3b));
incompatible.push_back(AZ_CRC("GraphCanvas_VisualService", 0xfbb2c871));
}
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
(void)dependent;
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("GraphCanvas_NodeService", 0xcc0f32cc));
required.push_back(AZ_CRC("GraphCanvas_StyledGraphicItemService", 0xeae4cdf4));
}
void Init() override;
void Activate() override;
void Deactivate() override;
////
// NodeNotifications
void OnNodeActivated();
void OnNodeWrapped(const AZ::EntityId& wrappingNode) override;
void OnNodeUnwrapped(const AZ::EntityId& wrappingNode) override;
////
private:
GeneralNodeFrameComponent(const GeneralNodeFrameComponent&) = delete;
const GeneralNodeFrameComponent& operator=(const GeneralNodeFrameComponent&) = delete;
bool m_shouldDeleteFrame;
GeneralNodeFrameGraphicsWidget* m_frameWidget;
};
//! The QGraphicsItem for the generic frame.
class GeneralNodeFrameGraphicsWidget
: public NodeFrameGraphicsWidget
{
public:
AZ_RTTI(GeneralNodeFrameGraphicsWidget, "{15200183-8316-4A7D-985E-5C3257CD2463}", NodeFrameGraphicsWidget);
AZ_CLASS_ALLOCATOR(GeneralNodeFrameGraphicsWidget, AZ::SystemAllocator, 0);
// Do not allow Serialization of Graphics Ui classes
static void Reflect(AZ::ReflectContext*) = delete;
GeneralNodeFrameGraphicsWidget(const AZ::EntityId& nodeVisual);
~GeneralNodeFrameGraphicsWidget() override = default;
// SceneMemberUIRequestBus
QPainterPath GetOutline() const override;
////
// QGraphicsWidget
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
////
};
}
@@ -0,0 +1,145 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <functional>
#include <AzCore/RTTI/TypeInfo.h>
#include <QGraphicsLayoutItem>
#include <QGraphicsGridLayout>
#include <Components/Nodes/General/GeneralNodeLayoutComponent.h>
#include <Components/Nodes/NodeComponent.h>
#include <Components/Nodes/NodeLayerControllerComponent.h>
#include <Components/Nodes/General/GeneralNodeFrameComponent.h>
#include <Components/Nodes/General/GeneralSlotLayoutComponent.h>
#include <Components/Nodes/General/GeneralNodeTitleComponent.h>
#include <Components/StylingComponent.h>
#include <GraphCanvas/Components/GeometryBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/tools.h>
#include <GraphCanvas/Styling/StyleHelper.h>
namespace GraphCanvas
{
///////////////////////////////
// GeneralNodeLayoutComponent
///////////////////////////////
void GeneralNodeLayoutComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<GeneralNodeLayoutComponent, NodeLayoutComponent>()
->Version(1)
;
}
}
AZ::Entity* GeneralNodeLayoutComponent::CreateGeneralNodeEntity(const char* nodeType, const NodeConfiguration& configuration)
{
// Create this Node's entity.
AZ::Entity* entity = NodeComponent::CreateCoreNodeEntity(configuration);
entity->CreateComponent<GeneralNodeFrameComponent>();
entity->CreateComponent<StylingComponent>(Styling::Elements::Node, AZ::EntityId(), nodeType);
entity->CreateComponent<GeneralNodeLayoutComponent>();
entity->CreateComponent<GeneralNodeTitleComponent>();
entity->CreateComponent<GeneralSlotLayoutComponent>();
entity->CreateComponent<NodeLayerControllerComponent>();
return entity;
}
GeneralNodeLayoutComponent::GeneralNodeLayoutComponent()
: m_title(nullptr)
, m_slots(nullptr)
{
}
GeneralNodeLayoutComponent::~GeneralNodeLayoutComponent()
{
}
void GeneralNodeLayoutComponent::Init()
{
NodeLayoutComponent::Init();
m_layout = new QGraphicsLinearLayout(Qt::Vertical);
m_layout->setInstantInvalidatePropagation(true);
m_title = new QGraphicsLinearLayout(Qt::Horizontal);
m_title->setInstantInvalidatePropagation(true);
m_slots = new QGraphicsLinearLayout(Qt::Horizontal);
m_slots->setInstantInvalidatePropagation(true);
}
void GeneralNodeLayoutComponent::Activate()
{
NodeLayoutComponent::Activate();
NodeNotificationBus::Handler::BusConnect(GetEntityId());
}
void GeneralNodeLayoutComponent::Deactivate()
{
NodeLayoutComponent::Deactivate();
StyleNotificationBus::Handler::BusDisconnect();
NodeNotificationBus::Handler::BusDisconnect();
}
void GeneralNodeLayoutComponent::OnStyleChanged()
{
UpdateLayoutParameters();
}
void GeneralNodeLayoutComponent::OnNodeActivated()
{
QGraphicsWidget* titleGraphicsItem = nullptr;
NodeTitleRequestBus::EventResult(titleGraphicsItem, GetEntityId(), &NodeTitleRequests::GetGraphicsWidget);
if (titleGraphicsItem)
{
m_title->addItem(titleGraphicsItem);
m_title->setContentsMargins(0, 0, 0, 0);
}
GetLayoutAs<QGraphicsLinearLayout>()->addItem(m_title);
QGraphicsLayoutItem* slotsGraphicsItem = nullptr;
NodeSlotsRequestBus::EventResult(slotsGraphicsItem, GetEntityId(), &NodeSlotsRequestBus::Events::GetGraphicsLayoutItem);
if (slotsGraphicsItem)
{
m_slots->addItem(slotsGraphicsItem);
m_slots->setContentsMargins(0, 0, 0, 0);
}
GetLayoutAs<QGraphicsLinearLayout>()->addItem(m_slots);
StyleNotificationBus::Handler::BusConnect(GetEntityId());
UpdateLayoutParameters();
}
void GeneralNodeLayoutComponent::UpdateLayoutParameters()
{
Styling::StyleHelper style(GetEntityId());
qreal border = style.GetAttribute(Styling::Attribute::BorderWidth, 0.0);
qreal spacing = style.GetAttribute(Styling::Attribute::Spacing, 4.0);
qreal margin = style.GetAttribute(Styling::Attribute::Margin, 4.0);
GetLayoutAs<QGraphicsLinearLayout>()->setContentsMargins(margin + border, margin + border, margin + border, margin + border);
GetLayoutAs<QGraphicsLinearLayout>()->setSpacing(spacing);
GetLayout()->invalidate();
}
}
@@ -0,0 +1,77 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <QGraphicsLinearLayout>
#include <AzCore/Component/Component.h>
#include <Components/Nodes/NodeLayoutComponent.h>
#include <GraphCanvas/Components/Nodes/NodeConfiguration.h>
#include <GraphCanvas/Components/Nodes/NodeLayoutBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Styling/StyleHelper.h>
class QGraphicsGridLayout;
namespace GraphCanvas
{
//! Lays out the parts of the generic Node
class GeneralNodeLayoutComponent
: public NodeLayoutComponent
, public NodeNotificationBus::Handler
, public StyleNotificationBus::Handler
{
public:
AZ_COMPONENT(GeneralNodeLayoutComponent, "{2AD34925-FF0E-4D0D-A371-6338FBAE0F43}", NodeLayoutComponent);
static void Reflect(AZ::ReflectContext*);
static AZ::Entity* CreateGeneralNodeEntity(const char* nodeType, const NodeConfiguration& nodeConfiguration = NodeConfiguration());
GeneralNodeLayoutComponent();
~GeneralNodeLayoutComponent();
// AZ::Component
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(NodeLayoutSupportServiceCrc);
dependent.push_back(AZ_CRC("GraphCanvas_TitleService", 0xfe6d63bc));
dependent.push_back(AZ_CRC("GraphCanvas_SlotsContainerService", 0x948b6696));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("GraphCanvas_NodeService", 0xcc0f32cc));
required.push_back(AZ_CRC("GraphCanvas_StyledGraphicItemService", 0xeae4cdf4));
}
void Init() override;
void Activate() override;
void Deactivate() override;
////
// StyleNotificationBus
void OnStyleChanged() override;
////
// NodeNotificationBus
void OnNodeActivated() override;
////
protected:
void UpdateLayoutParameters();
QGraphicsLinearLayout* m_title;
QGraphicsLinearLayout* m_slots;
};
}
@@ -0,0 +1,535 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <QGraphicsItem>
#include <QFont>
#include <QPainter>
#include <AzCore/Serialization/EditContext.h>
#include <Components/Nodes/General/GeneralNodeTitleComponent.h>
#include <Components/Nodes/General/GeneralNodeFrameComponent.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Editor/GraphCanvasProfiler.h>
#include <GraphCanvas/tools.h>
#include <GraphCanvas/Styling/StyleHelper.h>
#include <GraphCanvas/Utils/QtDrawingUtils.h>
#include <GraphCanvas/Components/VisualBus.h>
namespace GraphCanvas
{
//////////////////////////////
// GeneralNodeTitleComponent
//////////////////////////////
void GeneralNodeTitleComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<GeneralNodeTitleComponentSaveData, ComponentSaveData>()
->Version(1)
->Field("PaletteOverride", &GeneralNodeTitleComponentSaveData::m_paletteOverride)
;
serializeContext->Class<GeneralNodeTitleComponent, AZ::Component >()
->Version(4)
->Field("Title", &GeneralNodeTitleComponent::m_title)
->Field("SubTitle", &GeneralNodeTitleComponent::m_subTitle)
->Field("SaveData", &GeneralNodeTitleComponent::m_saveData)
->Field("DefaultPalette", &GeneralNodeTitleComponent::m_basePalette)
;
}
}
GeneralNodeTitleComponent::GeneralNodeTitleComponent()
{
}
void GeneralNodeTitleComponent::Init()
{
m_generalNodeTitleWidget = aznew GeneralNodeTitleGraphicsWidget(GetEntityId());
}
void GeneralNodeTitleComponent::Activate()
{
AZ::EntityId entityId = GetEntityId();
m_saveData.Activate(entityId);
SceneMemberNotificationBus::Handler::BusConnect(entityId);
NodeTitleRequestBus::Handler::BusConnect(entityId);
VisualNotificationBus::Handler::BusConnect(entityId);
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetTitle(m_title);
m_generalNodeTitleWidget->SetSubTitle(m_subTitle);
m_generalNodeTitleWidget->UpdateLayout();
m_generalNodeTitleWidget->Activate();
}
}
void GeneralNodeTitleComponent::Deactivate()
{
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->Deactivate();
}
SceneMemberNotificationBus::Handler::BusDisconnect();
NodeTitleRequestBus::Handler::BusDisconnect();
VisualNotificationBus::Handler::BusDisconnect();
}
void GeneralNodeTitleComponent::SetTitle(const AZStd::string& title)
{
m_title.SetFallback(title);
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetTitle(title);
}
}
void GeneralNodeTitleComponent::SetTranslationKeyedTitle(const TranslationKeyedString& title)
{
m_title = title;
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetTitle(title);
}
}
AZStd::string GeneralNodeTitleComponent::GetTitle() const
{
return m_title.GetDisplayString();
}
void GeneralNodeTitleComponent::SetSubTitle(const AZStd::string& subtitle)
{
m_subTitle.SetFallback(subtitle);
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetSubTitle(subtitle);
}
}
void GeneralNodeTitleComponent::SetTranslationKeyedSubTitle(const TranslationKeyedString& subtitle)
{
m_subTitle = subtitle;
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetSubTitle(subtitle);
}
}
AZStd::string GeneralNodeTitleComponent::GetSubTitle() const
{
return m_subTitle.GetDisplayString();
}
QGraphicsWidget* GeneralNodeTitleComponent::GetGraphicsWidget()
{
return m_generalNodeTitleWidget;
}
void GeneralNodeTitleComponent::SetDefaultPalette(const AZStd::string& basePalette)
{
if (m_generalNodeTitleWidget)
{
m_basePalette = basePalette;
m_generalNodeTitleWidget->SetPaletteOverride(basePalette);
}
}
void GeneralNodeTitleComponent::SetPaletteOverride(const AZStd::string& paletteOverride)
{
m_saveData.m_paletteOverride = paletteOverride;
m_saveData.SignalDirty();
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetPaletteOverride(paletteOverride);
}
}
void GeneralNodeTitleComponent::SetDataPaletteOverride(const AZ::Uuid& uuid)
{
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetPaletteOverride(uuid);
}
}
void GeneralNodeTitleComponent::SetColorPaletteOverride(const QColor& color)
{
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetPaletteOverride(color);
}
}
void GeneralNodeTitleComponent::ConfigureIconConfiguration(PaletteIconConfiguration& paletteConfiguration)
{
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->ConfigureIconConfiguration(paletteConfiguration);
}
}
void GeneralNodeTitleComponent::ClearPaletteOverride()
{
m_saveData.m_paletteOverride = "";
m_saveData.SignalDirty();
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->ClearPaletteOverride();
}
}
void GeneralNodeTitleComponent::OnSceneSet([[maybe_unused]] const AZ::EntityId& graphId)
{
if (!m_saveData.m_paletteOverride.empty())
{
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetPaletteOverride(m_saveData.m_paletteOverride);
}
}
else if (!m_basePalette.empty())
{
if (m_generalNodeTitleWidget)
{
m_generalNodeTitleWidget->SetPaletteOverride(m_basePalette);
}
}
}
///////////////////////////////////
// GeneralNodeTitleGraphicsWidget
///////////////////////////////////
GeneralNodeTitleGraphicsWidget::GeneralNodeTitleGraphicsWidget(const AZ::EntityId& entityId)
: m_entityId(entityId)
, m_paletteOverride(nullptr)
, m_colorOverride(nullptr)
{
setCacheMode(QGraphicsItem::CacheMode::DeviceCoordinateCache);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setGraphicsItem(this);
setAcceptHoverEvents(false);
setFlag(ItemIsMovable, false);
m_titleWidget = aznew GraphCanvasLabel(this);
m_subTitleWidget = aznew GraphCanvasLabel(this);
m_linearLayout = new QGraphicsLinearLayout(Qt::Vertical);
m_linearLayout->setSpacing(0);
setLayout(m_linearLayout);
setData(GraphicsItemName, QStringLiteral("Title/%1").arg(static_cast<AZ::u64>(entityId), 16, 16, QChar('0')));
}
GeneralNodeTitleGraphicsWidget::~GeneralNodeTitleGraphicsWidget()
{
delete m_colorOverride;
}
void GeneralNodeTitleGraphicsWidget::Activate()
{
AZ::EntityId entityId = GetEntityId();
SceneMemberNotificationBus::Handler::BusConnect(entityId);
NodeNotificationBus::Handler::BusConnect(entityId);
RootGraphicsItemNotificationBus::Handler::BusConnect(entityId);
AZ::EntityId scene;
SceneMemberRequestBus::EventResult(scene, entityId, &SceneMemberRequests::GetScene);
if (scene.IsValid())
{
SceneNotificationBus::Handler::BusConnect(scene);
UpdateStyles();
}
}
void GeneralNodeTitleGraphicsWidget::Deactivate()
{
SceneMemberNotificationBus::Handler::BusDisconnect();
RootGraphicsItemNotificationBus::Handler::BusDisconnect();
NodeNotificationBus::Handler::BusDisconnect();
SceneNotificationBus::Handler::BusDisconnect();
}
void GeneralNodeTitleGraphicsWidget::SetTitle(const TranslationKeyedString& title)
{
if (m_titleWidget)
{
m_titleWidget->SetLabel(title);
UpdateLayout();
}
}
void GeneralNodeTitleGraphicsWidget::SetSubTitle(const TranslationKeyedString& subtitle)
{
if (m_subTitleWidget)
{
m_subTitleWidget->SetLabel(subtitle);
UpdateLayout();
}
}
void GeneralNodeTitleGraphicsWidget::SetPaletteOverride(AZStd::string_view paletteOverride)
{
AZ_Error("GraphCanvas", m_colorOverride == nullptr, "Unsupported use of Color and Palete Overrides");
if (m_colorOverride)
{
delete m_colorOverride;
m_colorOverride = nullptr;
}
AZ::EntityId sceneId;
SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);
m_paletteOverride = nullptr;
StyleManagerRequestBus::BroadcastResult(m_paletteOverride, &StyleManagerRequests::FindColorPalette, paletteOverride);
update();
}
void GeneralNodeTitleGraphicsWidget::ConfigureIconConfiguration(PaletteIconConfiguration& paletteConfiguration)
{
bool isEnabled = false;
RootGraphicsItemRequestBus::EventResult(isEnabled, GetEntityId(), &RootGraphicsItemRequests::IsEnabled);
if (!isEnabled)
{
if (!m_disabledPalette)
{
StyleManagerRequestBus::BroadcastResult(m_disabledPalette, &StyleManagerRequests::FindColorPalette, "DisabledColorPalette");
}
if (m_disabledPalette)
{
m_disabledPalette->PopulatePaletteConfiguration(paletteConfiguration);
}
}
else if (m_colorOverride)
{
m_colorOverride->PopulatePaletteConfiguration(paletteConfiguration);
}
else if (m_paletteOverride)
{
m_paletteOverride->PopulatePaletteConfiguration(paletteConfiguration);
}
else
{
m_styleHelper.PopulatePaletteConfiguration(paletteConfiguration);
}
}
void GeneralNodeTitleGraphicsWidget::SetPaletteOverride(const AZ::Uuid& uuid)
{
AZ_Error("GraphCanvas", m_colorOverride == nullptr, "Unsupported use of Color and DataType Overrides");
if (m_colorOverride)
{
delete m_colorOverride;
m_colorOverride = nullptr;
}
AZ::EntityId sceneId;
SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);
m_paletteOverride = nullptr;
StyleManagerRequestBus::BroadcastResult(m_paletteOverride, &StyleManagerRequests::FindDataColorPalette, uuid);
update();
}
void GeneralNodeTitleGraphicsWidget::SetPaletteOverride(const QColor& color)
{
if (m_colorOverride == nullptr)
{
if (m_paletteOverride != nullptr)
{
m_paletteOverride = nullptr;
}
AZ::EntityId sceneId;
SceneMemberRequestBus::EventResult(sceneId, GetEntityId(), &SceneMemberRequests::GetScene);
m_colorOverride = new Styling::StyleHelper();
m_colorOverride->SetScene(sceneId);
m_colorOverride->SetStyle("ColorOverrideNodeTitlePalette");
}
if (m_colorOverride)
{
m_colorOverride->AddAttributeOverride(Styling::Attribute::BackgroundColor, color);
m_colorOverride->AddAttributeOverride(Styling::Attribute::LineColor, color);
update();
}
}
void GeneralNodeTitleGraphicsWidget::ClearPaletteOverride()
{
m_paletteOverride = nullptr;
update();
}
void GeneralNodeTitleGraphicsWidget::UpdateLayout()
{
while (m_linearLayout->count() != 0)
{
m_linearLayout->removeAt(0);
}
if (!m_titleWidget->GetLabel().empty())
{
m_linearLayout->addItem(m_titleWidget);
}
if (!m_subTitleWidget->GetLabel().empty())
{
m_linearLayout->addItem(m_subTitleWidget);
}
RefreshDisplay();
NodeTitleNotificationsBus::Event(GetEntityId(), &NodeTitleNotifications::OnTitleChanged);
NodeUIRequestBus::Event(GetEntityId(), &NodeUIRequests::AdjustSize);
}
void GeneralNodeTitleGraphicsWidget::UpdateStyles()
{
m_styleHelper.SetStyle(GetEntityId(), Styling::Elements::Title);
m_titleWidget->SetStyle(GetEntityId(), Styling::Elements::MainTitle);
m_subTitleWidget->SetStyle(GetEntityId(), Styling::Elements::SubTitle);
// Just clear our the disabled palette and we'll get it when we need it.
m_disabledPalette = nullptr;
}
void GeneralNodeTitleGraphicsWidget::RefreshDisplay()
{
updateGeometry();
update();
}
void GeneralNodeTitleGraphicsWidget::OnStylesChanged()
{
UpdateStyles();
RefreshDisplay();
}
void GeneralNodeTitleGraphicsWidget::OnAddedToScene(const AZ::EntityId& scene)
{
SceneNotificationBus::Handler::BusConnect(scene);
UpdateStyles();
RefreshDisplay();
}
void GeneralNodeTitleGraphicsWidget::OnRemovedFromScene([[maybe_unused]] const AZ::EntityId& scene)
{
SceneNotificationBus::Handler::BusDisconnect();
}
void GeneralNodeTitleGraphicsWidget::OnTooltipChanged(const AZStd::string& tooltip)
{
setToolTip(Tools::qStringFromUtf8(tooltip));
}
void GeneralNodeTitleGraphicsWidget::OnEnabledChanged([[maybe_unused]] RootGraphicsItemEnabledState enabledState)
{
UpdateStyles();
RefreshDisplay();
}
void GeneralNodeTitleGraphicsWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
Styling::PaletteStyle style = m_styleHelper.GetAttribute(Styling::Attribute::PaletteStyle, Styling::PaletteStyle::Solid);
if (m_paletteOverride)
{
style = m_paletteOverride->GetAttribute(Styling::Attribute::PaletteStyle, Styling::PaletteStyle::Solid);
}
// Background
QRectF bounds = boundingRect();
qreal cornerRadius = 0.0;
NodeUIRequestBus::EventResult(cornerRadius, GetEntityId(), &NodeUIRequests::GetCornerRadius);
// Ensure the bounds are large enough to draw the full radius
// Even in our smaller section
if (bounds.height() < 2.0 * cornerRadius)
{
bounds.setHeight(2.0 * cornerRadius);
}
QPainterPath path;
path.setFillRule(Qt::WindingFill);
// -1.0 because the rounding is a little bit short(for some reason), so I subtract one and let it overshoot a smidge.
path.addRoundedRect(bounds, cornerRadius - 1.0, cornerRadius - 1.0);
// We only want corners on the top half. So we need to draw a rectangle over the bottom bits to square it out.
QPointF bottomTopLeft(bounds.bottomLeft());
bottomTopLeft.setY(bottomTopLeft.y() - cornerRadius - 1.0);
path.addRect(QRectF(bottomTopLeft, bounds.bottomRight()));
painter->save();
painter->setClipPath(path);
bool isEnabled = false;
RootGraphicsItemRequestBus::EventResult(isEnabled, GetEntityId(), &RootGraphicsItemRequests::IsEnabled);
if (!isEnabled)
{
if (!m_disabledPalette)
{
StyleManagerRequestBus::BroadcastResult(m_disabledPalette, &StyleManagerRequests::FindColorPalette, "DisabledColorPalette");
}
if (m_disabledPalette)
{
QtDrawingUtils::FillArea((*painter), path.boundingRect(), (*m_disabledPalette));
}
}
else if (m_colorOverride)
{
QtDrawingUtils::FillArea((*painter), path.boundingRect(), (*m_colorOverride));
}
else if (m_paletteOverride)
{
QtDrawingUtils::FillArea((*painter), path.boundingRect(), (*m_paletteOverride));
}
else
{
QtDrawingUtils::FillArea((*painter), path.boundingRect(), m_styleHelper);
}
QLinearGradient gradient(bounds.bottomLeft(), bounds.topLeft());
gradient.setColorAt(0, QColor(0, 0, 0, 102));
gradient.setColorAt(1, QColor(0, 0, 0, 77));
painter->fillPath(path, gradient);
painter->restore();
QGraphicsWidget::paint(painter, option, widget);
}
}
@@ -0,0 +1,186 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <QGraphicsLinearLayout>
#include <QGraphicsWidget>
#include <AzCore/Component/Component.h>
#include <GraphCanvas/Components/GraphCanvasPropertyBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/Types/EntitySaveData.h>
#include <GraphCanvas/Types/TranslationTypes.h>
#include <Widgets/GraphCanvasLabel.h>
namespace GraphCanvas
{
class GeneralNodeTitleGraphicsWidget;
//! The Title component gives a Node the ability to display a title.
class GeneralNodeTitleComponent
: public AZ::Component
, public NodeTitleRequestBus::Handler
, public SceneMemberNotificationBus::Handler
, public VisualNotificationBus::Handler
{
public:
AZ_COMPONENT(GeneralNodeTitleComponent, "{67D54B26-A924-4028-8544-5684B16BF04A}");
static void Reflect(AZ::ReflectContext*);
GeneralNodeTitleComponent();
~GeneralNodeTitleComponent() = default;
// AZ::Component
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(NodeTitleServiceCrc);
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(NodeTitleServiceCrc);
}
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
(void)dependent;
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("GraphCanvas_StyledGraphicItemService", 0xeae4cdf4));
required.push_back(AZ_CRC("GraphCanvas_SceneMemberService", 0xe9759a2d));
}
void Init() override;
void Activate() override;
void Deactivate() override;
////
// NodeTitleRequestBus
void SetTitle(const AZStd::string& title) override;
void SetTranslationKeyedTitle(const TranslationKeyedString& title) override;
AZStd::string GetTitle() const override;
void SetSubTitle(const AZStd::string& subtitle) override;
void SetTranslationKeyedSubTitle(const TranslationKeyedString& subtitle) override;
AZStd::string GetSubTitle() const override;
QGraphicsWidget* GetGraphicsWidget() override;
void SetDefaultPalette(const AZStd::string& palette) override;
void SetPaletteOverride(const AZStd::string& paletteOverride) override;
void SetDataPaletteOverride(const AZ::Uuid& uuid) override;
void SetColorPaletteOverride(const QColor& color) override;
void ConfigureIconConfiguration(PaletteIconConfiguration& paletteConfiguration) override;
void ClearPaletteOverride() override;
/////
// SceneMemberNotificationBus
void OnSceneSet(const AZ::EntityId& graphId) override;
////
private:
GeneralNodeTitleComponent(const GeneralNodeTitleComponent&) = delete;
TranslationKeyedString m_title;
TranslationKeyedString m_subTitle;
AZStd::string m_basePalette;
GeneralNodeTitleComponentSaveData m_saveData;
GeneralNodeTitleGraphicsWidget* m_generalNodeTitleWidget = nullptr;
};
//! The Title QGraphicsWidget for displaying a title
class GeneralNodeTitleGraphicsWidget
: public QGraphicsWidget
, public SceneNotificationBus::Handler
, public SceneMemberNotificationBus::Handler
, public NodeNotificationBus::Handler
, public RootGraphicsItemNotificationBus::Handler
{
public:
AZ_TYPE_INFO(GeneralNodeTitleGraphicsWidget, "{9DE7D3C0-D88C-47D8-85D4-5E0F619E60CB}");
AZ_CLASS_ALLOCATOR(GeneralNodeTitleGraphicsWidget, AZ::SystemAllocator, 0);
GeneralNodeTitleGraphicsWidget(const AZ::EntityId& entityId);
~GeneralNodeTitleGraphicsWidget() override;
void Activate();
void Deactivate();
void SetTitle(const TranslationKeyedString& title);
void SetSubTitle(const TranslationKeyedString& subtitle);
void SetPaletteOverride(AZStd::string_view paletteOverride);
void SetPaletteOverride(const AZ::Uuid& uuid);
void SetPaletteOverride(const QColor& color);
void ConfigureIconConfiguration(PaletteIconConfiguration& paletteConfiguration);
void ClearPaletteOverride();
void UpdateLayout();
void UpdateStyles();
void RefreshDisplay();
// SceneNotificationBus
void OnStylesChanged() override;
////
// SceneMemberNotificationBus
void OnAddedToScene(const AZ::EntityId& scene) override;
void OnRemovedFromScene(const AZ::EntityId& scene) override;
////
// NodeNotificationBus
void OnTooltipChanged(const AZStd::string& tooltip) override;
////
// RootGraphicsItemNotifications
void OnEnabledChanged(RootGraphicsItemEnabledState enabledState) override;
////
protected:
// QGraphicsItem
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override;
const AZ::EntityId& GetEntityId() const { return m_entityId; }
private:
GeneralNodeTitleGraphicsWidget(const GeneralNodeTitleGraphicsWidget&) = delete;
QGraphicsLinearLayout* m_linearLayout;
GraphCanvasLabel* m_titleWidget;
GraphCanvasLabel* m_subTitleWidget;
AZ::EntityId m_entityId;
const Styling::StyleHelper* m_disabledPalette;
const Styling::StyleHelper* m_paletteOverride;
Styling::StyleHelper* m_colorOverride;
Styling::StyleHelper m_styleHelper;
};
}
@@ -0,0 +1,823 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <functional>
#include <QGraphicsItem>
#include <QGraphicsLayoutItem>
#include <QGraphicsScene>
#include <QGraphicsWidget>
#include <QPainter>
#include <AzCore/RTTI/TypeInfo.h>
#include <Components/Nodes/General/GeneralSlotLayoutComponent.h>
#include <Components/Nodes/General/GeneralNodeFrameComponent.h>
#include <GraphCanvas/Components/GeometryBus.h>
#include <GraphCanvas/Components/Nodes/NodeLayoutBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Editor/GraphCanvasProfiler.h>
#include <GraphCanvas/tools.h>
namespace GraphCanvas
{
///////////////////////////////
// GeneralSlotLayoutComponent
///////////////////////////////
void GeneralSlotLayoutComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<SlotGroupConfiguration>()
->Version(1)
->Field("LayoutOrder", &SlotGroupConfiguration::m_layoutOrder)
;
serializeContext->Class<GeneralSlotLayoutComponent, AZ::Component>()
->Version(2)
->Field("EnableDividers", &GeneralSlotLayoutComponent::m_enableDividers)
->Field("ConfigurationMap", &GeneralSlotLayoutComponent::m_slotGroupConfigurations)
;
}
}
GeneralSlotLayoutComponent::GeneralSlotLayoutComponent()
: m_enableDividers(false)
{
}
GeneralSlotLayoutComponent::~GeneralSlotLayoutComponent()
{
}
void GeneralSlotLayoutComponent::Init()
{
m_nodeSlotsUi = aznew GeneralSlotLayoutGraphicsWidget(*this);
// Default ordering of the slots determined by the SlotGroupConfiguration value.
// Values are displayed lowest first to highest last.
//
// This needs to be done using emplace and in init to deal with serialization(serialization will not step on any values
// already in this map, so they all need to be added after the deserialization step has occurred)
m_slotGroupConfigurations.emplace(SlotGroups::ExecutionGroup, SlotGroupConfiguration(0));
m_slotGroupConfigurations.emplace(SlotGroups::PropertyGroup, SlotGroupConfiguration(1));
m_slotGroupConfigurations.emplace(SlotGroups::VariableReferenceGroup, SlotGroupConfiguration(2));
m_slotGroupConfigurations.emplace(SlotGroups::DataGroup, SlotGroupConfiguration(3));
m_slotGroupConfigurations.emplace(SlotGroups::VariableSourceGroup, SlotGroupConfiguration(4));
}
void GeneralSlotLayoutComponent::Activate()
{
if (m_nodeSlotsUi)
{
m_nodeSlotsUi->Activate();
}
}
void GeneralSlotLayoutComponent::Deactivate()
{
if (m_nodeSlotsUi)
{
m_nodeSlotsUi->Deactivate();
}
}
QGraphicsWidget* GeneralSlotLayoutComponent::GetGraphicsWidget()
{
return m_nodeSlotsUi;
}
////////////////////////////////////
// GeneralSlotLayoutGraphicsWidget
////////////////////////////////////
////////////////////////
// LayoutDividerWidget
////////////////////////
GeneralSlotLayoutGraphicsWidget::LayoutDividerWidget::LayoutDividerWidget(QGraphicsItem* parent)
: QGraphicsWidget(parent)
{
setAutoFillBackground(true);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
setContentsMargins(0, 0, 0, 0);
}
void GeneralSlotLayoutGraphicsWidget::LayoutDividerWidget::UpdateStyle(const Styling::StyleHelper& styleHelper)
{
prepareGeometryChange();
qreal border = AZStd::max(1., styleHelper.GetAttribute(Styling::Attribute::BorderWidth, 0.));
QColor dividerColor = styleHelper.GetColor(Styling::Attribute::BorderColor);
QPalette widgetPalette = palette();
widgetPalette.setColor(QPalette::ColorGroup::Active, QPalette::ColorRole::Window, dividerColor);
setPalette(widgetPalette);
setMinimumHeight(border);
setPreferredHeight(border);
setMaximumHeight(border);
updateGeometry();
update();
}
//////////////////////////
// LinearSlotGroupLayout
//////////////////////////
GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::LinearSlotGroupWidget(QGraphicsItem* parent)
: QGraphicsWidget(parent)
, m_inputs(nullptr)
, m_outputs(nullptr)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_inputs = new QGraphicsLinearLayout(Qt::Vertical);
m_inputs->setContentsMargins(0, 0, 0, 0);
m_inputs->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
{
QGraphicsWidget* spacer = new QGraphicsWidget();
spacer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
spacer->setContentsMargins(0, 0, 0, 0);
spacer->setPreferredSize(0, 0);
m_inputs->addItem(spacer);
}
m_outputs = new QGraphicsLinearLayout(Qt::Vertical);
m_outputs->setContentsMargins(0, 0, 0, 0);
m_outputs->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
{
QGraphicsWidget* spacer = new QGraphicsWidget();
spacer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
spacer->setContentsMargins(0, 0, 0, 0);
spacer->setPreferredSize(0, 0);
m_outputs->addItem(spacer);
}
QGraphicsLinearLayout* layout = new QGraphicsLinearLayout(Qt::Horizontal);
setLayout(layout);
layout->setInstantInvalidatePropagation(true);
// Creating the actual display
// <input><spacer><output>
layout->addItem(m_inputs);
m_horizontalSpacer = new QGraphicsWidget();
m_horizontalSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_horizontalSpacer->setContentsMargins(0, 0, 0, 0);
m_horizontalSpacer->setPreferredSize(0, 0);
layout->addItem(m_horizontalSpacer);
layout->addItem(m_outputs);
layout->setAlignment(m_outputs, Qt::AlignRight);
}
void GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::DisplaySlot(const AZ::EntityId& slotId)
{
ConnectionType connectionType = ConnectionType::CT_Invalid;
SlotRequestBus::EventResult(connectionType, slotId, &SlotRequests::GetConnectionType);
int layoutOrder = 0;
SlotLayoutInfo slotInfo(slotId);
if (connectionType == CT_Input)
{
SlotUINotificationBus::MultiHandler::BusConnect(slotId);
m_inputSlotSet.insert(slotId);
layoutOrder = LayoutSlot(m_inputs, m_inputSlots, slotInfo);
}
else if (connectionType == CT_Output)
{
SlotUINotificationBus::MultiHandler::BusConnect(slotId);
m_outputSlotSet.insert(slotId);
LayoutSlot(m_outputs, m_outputSlots, slotInfo);
}
else
{
AZ_Warning("GraphCanvas", false, "Invalid Connection Type for slot. Cannot add to Node Layout");
}
}
void GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::RemoveSlot(const AZ::EntityId& slotId)
{
ConnectionType connectionType = ConnectionType::CT_Invalid;
SlotRequestBus::EventResult(connectionType, slotId, &SlotRequests::GetConnectionType);
QGraphicsLayoutItem* layoutItem = GetLayoutItem(slotId);
if (layoutItem)
{
SlotUINotificationBus::MultiHandler::BusDisconnect(slotId);
if (scene())
{
scene()->removeItem(layoutItem->graphicsItem());
}
if (connectionType == CT_Input)
{
m_inputSlotSet.erase(slotId);
m_inputs->removeItem(layoutItem);
for (unsigned int i = 0; i < m_inputSlots.size(); ++i)
{
if (m_inputSlots[i].m_slotId == slotId)
{
m_inputSlots.erase(m_inputSlots.begin() + i);
break;
}
}
}
else if (connectionType == CT_Output)
{
m_outputSlotSet.erase(slotId);
m_outputs->removeItem(layoutItem);
for (unsigned int i = 0; i < m_outputSlots.size(); ++i)
{
if (m_outputSlots[i].m_slotId == slotId)
{
m_outputSlots.erase(m_outputSlots.begin() + i);
break;
}
}
}
}
}
const AZStd::vector< SlotLayoutInfo >& GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::GetInputSlots() const
{
return m_inputSlots;
}
const AZStd::vector< SlotLayoutInfo >& GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::GetOutputSlots() const
{
return m_outputSlots;
}
bool GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::IsEmpty() const
{
// 1 because there is a spacer in each of the layouts that we need to account for.
return m_inputs->count() == 1 && m_outputs->count() == 1;
}
void GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::UpdateStyle(const Styling::StyleHelper& styleHelper)
{
prepareGeometryChange();
qreal spacing = styleHelper.GetAttribute(Styling::Attribute::Spacing, 0);
qreal margin = styleHelper.GetAttribute(Styling::Attribute::Margin, 0);
setContentsMargins(margin, margin, margin, margin);
for (QGraphicsLinearLayout* internalLayout : { m_inputs, m_outputs })
{
internalLayout->setSpacing(spacing);
internalLayout->invalidate();
internalLayout->updateGeometry();
}
updateGeometry();
update();
}
void GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::OnSlotLayoutPriorityChanged(int layoutPriority)
{
const SlotId* slotId = SlotUINotificationBus::GetCurrentBusId();
if (slotId == nullptr)
{
return;
}
AZStd::vector< SlotLayoutInfo >* layoutVector = nullptr;
QGraphicsLinearLayout* layoutElement = nullptr;
if (m_inputSlotSet.count((*slotId)) > 0)
{
layoutVector = &m_inputSlots;
layoutElement = m_inputs;
}
else if (m_outputSlotSet.count((*slotId)) > 0)
{
layoutVector = &m_outputSlots;
layoutElement = m_outputs;
}
if (layoutVector == nullptr || layoutElement == nullptr)
{
return;
}
for (auto layoutIter = layoutVector->begin(); layoutIter != layoutVector->end(); ++layoutIter)
{
if (layoutIter->m_slotId == (*slotId))
{
SlotLayoutInfo info = (*layoutIter);
info.m_priority = layoutPriority;
layoutVector->erase(layoutIter);
QGraphicsLayoutItem* layoutItem = GetLayoutItem(info.m_slotId);
layoutElement->removeItem(layoutItem);
LayoutSlot(layoutElement, (*layoutVector), info);
break;
}
}
}
int GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::LayoutSlot(QGraphicsLinearLayout* layout, AZStd::vector<SlotLayoutInfo>& slotList, const SlotLayoutInfo& slotInfo)
{
bool inserted = false;
int i = 0;
auto listIter = slotList.begin();
while (listIter != slotList.end())
{
if (listIter->m_priority < slotInfo.m_priority)
{
inserted = true;
slotList.insert(listIter, slotInfo);
break;
}
++i;
++listIter;
}
if (!inserted)
{
slotList.insert(slotList.end(), slotInfo);
}
QGraphicsLayoutItem* layoutItem = GetLayoutItem(slotInfo.m_slotId);
if (layoutItem)
{
layout->insertItem(i, layoutItem);
layout->setAlignment(layoutItem, Qt::AlignTop);
SlotRequestBus::Event(slotInfo.m_slotId, &SlotRequests::SetDisplayOrdering, i);
}
return i;
}
QGraphicsLayoutItem* GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget::GetLayoutItem(const AZ::EntityId& slotId) const
{
QGraphicsLayoutItem* layoutItem = nullptr;
VisualRequestBus::EventResult(layoutItem, slotId, &VisualRequests::AsGraphicsLayoutItem);
AZ_Assert(layoutItem, "Slot must return a GraphicsLayoutItem.");
return layoutItem;
}
////////////////////////////////////
// GeneralSlotLayoutGraphicsWidget
////////////////////////////////////
GeneralSlotLayoutGraphicsWidget::GeneralSlotLayoutGraphicsWidget(GeneralSlotLayoutComponent& nodeSlots)
: m_nodeSlots(nodeSlots)
, m_entityId(nodeSlots.GetEntityId())
, m_addedToScene(false)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
setGraphicsItem(this);
setAcceptHoverEvents(false);
setFlag(ItemIsMovable, false);
setContentsMargins(0, 0, 0, 0);
setData(GraphicsItemName, QStringLiteral("Slots/%1").arg(static_cast<AZ::u64>(GetEntityId()), 16, 16, QChar('0')));
m_groupLayout = new QGraphicsLinearLayout(Qt::Vertical);
m_groupLayout->setSpacing(0);
m_groupLayout->setInstantInvalidatePropagation(true);
setLayout(m_groupLayout);
}
GeneralSlotLayoutGraphicsWidget::~GeneralSlotLayoutGraphicsWidget()
{
// Because I'm allowing for re-use of widgets. There's no guarantee which widgets
// will have a valid parent. So we want to clear our display, then delete everything
// that we own.
ClearLayout();
for (LayoutDividerWidget* divider : m_dividers)
{
delete divider;
}
for (auto& mapPair : m_slotGroups)
{
delete mapPair.second;
}
}
void GeneralSlotLayoutGraphicsWidget::Activate()
{
SlotLayoutRequestBus::Handler::BusConnect(GetEntityId());
NodeNotificationBus::Handler::BusConnect(GetEntityId());
NodeSlotsRequestBus::Handler::BusConnect(GetEntityId());
StyleNotificationBus::Handler::BusConnect(GetEntityId());
SceneMemberNotificationBus::Handler::BusConnect(GetEntityId());
}
void GeneralSlotLayoutGraphicsWidget::OnNodeActivated()
{
ActivateSlots();
UpdateLayout();
}
void GeneralSlotLayoutGraphicsWidget::Deactivate()
{
ClearLayout();
SceneMemberNotificationBus::Handler::BusDisconnect();
StyleNotificationBus::Handler::BusDisconnect();
NodeSlotsRequestBus::Handler::BusDisconnect();
NodeNotificationBus::Handler::BusDisconnect();
SlotLayoutRequestBus::Handler::BusDisconnect();
}
void GeneralSlotLayoutGraphicsWidget::OnSlotAddedToNode(const AZ::EntityId& slotId)
{
bool needsUpate = DisplaySlot(slotId);
if (needsUpate)
{
UpdateLayout();
}
}
void GeneralSlotLayoutGraphicsWidget::OnSlotRemovedFromNode(const AZ::EntityId& slotId)
{
bool needsUpdate = RemoveSlot(slotId);
if (needsUpdate)
{
UpdateLayout();
}
}
QGraphicsLayoutItem* GeneralSlotLayoutGraphicsWidget::GetGraphicsLayoutItem()
{
return this;
}
void GeneralSlotLayoutGraphicsWidget::OnSceneSet([[maybe_unused]] const AZ::EntityId& sceneId)
{
m_addedToScene = true;
UpdateLayout();
}
void GeneralSlotLayoutGraphicsWidget::SetDividersEnabled(bool enabled)
{
m_nodeSlots.m_enableDividers = enabled;
UpdateLayout();
}
void GeneralSlotLayoutGraphicsWidget::ConfigureSlotGroup(SlotGroup group, SlotGroupConfiguration configuration)
{
if (group != SlotGroups::Invalid)
{
m_nodeSlots.m_slotGroupConfigurations[group] = configuration;
UpdateLayout();
}
}
int GeneralSlotLayoutGraphicsWidget::GetSlotGroupDisplayOrder(SlotGroup group) const
{
AZStd::set<SlotGroup, SlotGroupConfigurationComparator> slotOrdering(SlotGroupConfigurationComparator(&m_nodeSlots.m_slotGroupConfigurations));
for (auto& mapPair : m_slotGroups)
{
if (!mapPair.second->IsEmpty())
{
const SlotGroupConfiguration& configuration = m_nodeSlots.m_slotGroupConfigurations[mapPair.first];
if (configuration.m_visible)
{
slotOrdering.insert(mapPair.first);
}
}
}
int counter = 1;
for (const SlotGroup& slotGroup : slotOrdering)
{
if (slotGroup == group)
{
return counter;
}
++counter;
}
return -1;
}
bool GeneralSlotLayoutGraphicsWidget::IsSlotGroupVisible(SlotGroup group) const
{
bool isVisible = false;
if (group != SlotGroups::Invalid)
{
SlotGroupConfiguration& slotConfiguration = m_nodeSlots.m_slotGroupConfigurations[group];
isVisible = slotConfiguration.m_visible;
}
return isVisible;
}
void GeneralSlotLayoutGraphicsWidget::SetSlotGroupVisible(SlotGroup group, bool visible)
{
if (group != SlotGroups::Invalid)
{
SlotGroupConfiguration& slotConfiguration = m_nodeSlots.m_slotGroupConfigurations[group];
if (slotConfiguration.m_visible != visible)
{
slotConfiguration.m_visible = visible;
UpdateLayout();
}
}
}
void GeneralSlotLayoutGraphicsWidget::ClearSlotGroup(SlotGroup group)
{
if (group != SlotGroups::Invalid)
{
LinearSlotGroupWidget* slotGroupWidget = FindCreateSlotGroupWidget(group);
if (slotGroupWidget)
{
AZStd::vector< SlotLayoutInfo > inputSlots = slotGroupWidget->GetInputSlots();
for (const SlotLayoutInfo& inputSlot : inputSlots)
{
NodeRequestBus::Event(GetEntityId(), &NodeRequests::RemoveSlot, inputSlot.m_slotId);
}
AZStd::vector< SlotLayoutInfo > outputSlots = slotGroupWidget->GetOutputSlots();
for (const SlotLayoutInfo& outputSlot : outputSlots)
{
NodeRequestBus::Event(GetEntityId(), &NodeRequests::RemoveSlot, outputSlot.m_slotId);
}
}
}
}
void GeneralSlotLayoutGraphicsWidget::OnStyleChanged()
{
UpdateStyles();
update();
}
bool GeneralSlotLayoutGraphicsWidget::DisplaySlot(const AZ::EntityId& slotId)
{
bool needsUpdate = false;
SlotGroup slotGroup = SlotGroups::Invalid;
SlotRequestBus::EventResult(slotGroup, slotId, &SlotRequests::GetSlotGroup);
LinearSlotGroupWidget* groupWidget = FindCreateSlotGroupWidget(slotGroup);
if (groupWidget)
{
needsUpdate = groupWidget->IsEmpty();
groupWidget->DisplaySlot(slotId);
}
return needsUpdate;
}
bool GeneralSlotLayoutGraphicsWidget::RemoveSlot(const AZ::EntityId& slotId)
{
bool needsUpdate = false;
SlotGroup slotGroup = SlotGroups::Invalid;
SlotRequestBus::EventResult(slotGroup, slotId, &SlotRequests::GetSlotGroup);
LinearSlotGroupWidget* groupWidget = FindCreateSlotGroupWidget(slotGroup);
if (groupWidget)
{
groupWidget->RemoveSlot(slotId);
needsUpdate = groupWidget->IsEmpty();
}
return needsUpdate;
}
void GeneralSlotLayoutGraphicsWidget::ActivateSlots()
{
AZStd::vector<AZ::EntityId> slotIds;
NodeRequestBus::EventResult(slotIds, m_nodeSlots.GetEntityId(), &NodeRequests::GetSlotIds);
for (const AZ::EntityId& slotId : slotIds)
{
AZ::Entity* slot = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(slot, &AZ::ComponentApplicationRequests::FindEntity, slotId);
AZ_Assert(slot, "A Slot (ID: %s) of Node (ID: %s) has no Entity!", slotId.ToString().data(), m_nodeSlots.GetEntityId().ToString().data());
DisplaySlot(slotId);
}
}
void GeneralSlotLayoutGraphicsWidget::ClearLayout()
{
// Clear out our previous configurations
while (m_groupLayout->count() > 0)
{
m_groupLayout->removeAt(m_groupLayout->count() - 1);
}
}
void GeneralSlotLayoutGraphicsWidget::UpdateLayout()
{
if (!m_addedToScene)
{
return;
}
ClearLayout();
prepareGeometryChange();
AZStd::set<SlotGroup, SlotGroupConfigurationComparator> slotOrdering(SlotGroupConfigurationComparator(&m_nodeSlots.m_slotGroupConfigurations));
for (auto& mapPair : m_slotGroups)
{
if (!mapPair.second->IsEmpty())
{
const SlotGroupConfiguration& configuration = m_nodeSlots.m_slotGroupConfigurations[mapPair.first];
if (configuration.m_visible)
{
slotOrdering.insert(mapPair.first);
}
else
{
// Fun with SceneFilter's.
// If an object with a scene filter is removed from the scene
// That scene filter gets destroyed or invalidated in some way.
//
// This means if we were to ever remove a data slot from the scene
// and add it back on, we need to re-hook up everything.
auto groupIter = m_slotGroups.find(mapPair.first);
if (groupIter != m_slotGroups.end())
{
if (groupIter->second->scene() != nullptr)
{
groupIter->second->scene()->removeItem(groupIter->second);
}
}
}
}
}
for (auto& divider : m_dividers)
{
if (divider->isVisible())
{
divider->setVisible(false);
}
}
int dividerCount = 0;
bool needsDivider = false;
for (const SlotGroup& slotGroup : slotOrdering)
{
if (needsDivider)
{
needsDivider = false;
LayoutDividerWidget* dividerWidget = FindCreateDividerWidget(dividerCount);
++dividerCount;
m_groupLayout->addItem(dividerWidget);
dividerWidget->setVisible(true);
}
auto groupIter = m_slotGroups.find(slotGroup);
if (groupIter != m_slotGroups.end())
{
needsDivider = m_nodeSlots.m_enableDividers;
m_groupLayout->addItem(groupIter->second);
}
}
RefreshDisplay();
NodeUIRequestBus::Event(GetEntityId(), &NodeUIRequests::AdjustSize);
}
void GeneralSlotLayoutGraphicsWidget::UpdateStyles()
{
m_styleHelper.SetStyle(GetEntityId(), Styling::Elements::GeneralSlotLayout);
prepareGeometryChange();
qreal margin = m_styleHelper.GetAttribute(Styling::Attribute::Margin, 0.0);
m_groupLayout->setContentsMargins(margin, margin, margin, margin);
m_groupLayout->setSpacing(m_styleHelper.GetAttribute(Styling::Attribute::Spacing, 0.0));
for (LayoutDividerWidget* divider : m_dividers)
{
divider->UpdateStyle(m_styleHelper);
}
for (auto& mapPair : m_slotGroups)
{
mapPair.second->UpdateStyle(m_styleHelper);
}
RefreshDisplay();
}
void GeneralSlotLayoutGraphicsWidget::RefreshDisplay()
{
updateGeometry();
update();
}
GeneralSlotLayoutGraphicsWidget::LinearSlotGroupWidget* GeneralSlotLayoutGraphicsWidget::FindCreateSlotGroupWidget(SlotGroup slotType)
{
AZ_Warning("GraphCanvas", slotType != SlotGroups::Invalid, "Trying to Create a Slot Group for an Invalid slot group");
LinearSlotGroupWidget* retVal = nullptr;
if (slotType != SlotGroups::Invalid)
{
auto mapIter = m_slotGroups.find(slotType);
if (mapIter != m_slotGroups.end())
{
retVal = mapIter->second;
}
else
{
auto configurationIter = m_nodeSlots.m_slotGroupConfigurations.find(slotType);
if (configurationIter == m_nodeSlots.m_slotGroupConfigurations.end())
{
SlotGroupConfiguration groupConfiguration;
groupConfiguration.m_layoutOrder = static_cast<int>(m_nodeSlots.m_slotGroupConfigurations.size());
m_nodeSlots.m_slotGroupConfigurations[slotType] = groupConfiguration;
}
retVal = aznew LinearSlotGroupWidget(this);
retVal->UpdateStyle(m_styleHelper);
m_slotGroups[slotType] = retVal;
}
}
return retVal;
}
GeneralSlotLayoutGraphicsWidget::LayoutDividerWidget* GeneralSlotLayoutGraphicsWidget::FindCreateDividerWidget(int index)
{
AZ_Error("GraphCanvas", index <= m_dividers.size(), "Invalid Divider Creation flow. Jumped the line in divider indexing.");
LayoutDividerWidget* retVal = nullptr;
while (index >= m_dividers.size())
{
// Create and configure a new divider
LayoutDividerWidget* divider = aznew LayoutDividerWidget(this);
divider->UpdateStyle(m_styleHelper);
m_dividers.push_back(divider);
}
retVal = m_dividers[index];
return retVal;
}
}
@@ -0,0 +1,216 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <QGraphicsLinearLayout>
#include <QGraphicsWidget>
#include <AzCore/Component/Component.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/Nodes/NodeLayoutBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Styling/StyleHelper.h>
namespace GraphCanvas
{
class GeneralSlotLayoutGraphicsWidget;
//! Lays out the slots for the General Node
class GeneralSlotLayoutComponent
: public AZ::Component
{
public:
AZ_COMPONENT(GeneralSlotLayoutComponent, "{F6554B50-A42A-4C79-8B1D-547EEA1EA52D}");
static void Reflect(AZ::ReflectContext*);
GeneralSlotLayoutComponent();
~GeneralSlotLayoutComponent();
// AZ::Component
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("GraphCanvas_SlotsContainerService", 0x948b6696));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incombatible)
{
incombatible.push_back(AZ_CRC("GraphCanvas_SlotsContainerService", 0x948b6696));
}
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
(void)dependent;
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("GraphCanvas_StyledGraphicItemService", 0xeae4cdf4));
required.push_back(AZ_CRC("GraphCanvas_SceneMemberService", 0xe9759a2d));
}
void Init() override;
void Activate() override;
void Deactivate() override;
////
// NodeSlotsRequestBus
QGraphicsWidget* GetGraphicsWidget();
////
private:
GeneralSlotLayoutComponent(const GeneralSlotLayoutComponent&) = delete;
bool m_enableDividers;
SlotGroupConfigurationMap m_slotGroupConfigurations;
friend class GeneralSlotLayoutGraphicsWidget;
GeneralSlotLayoutGraphicsWidget* m_nodeSlotsUi;
};
//! The slots QGraphicsWidget for displaying a the node slots
//! QtWidgets cannot be serialized out, so the component wrapper
//! stores the actual configuration map for serialization.
class GeneralSlotLayoutGraphicsWidget
: public QGraphicsWidget
, public SlotLayoutRequestBus::Handler
, public NodeSlotsRequestBus::Handler
, public NodeNotificationBus::Handler
, public StyleNotificationBus::Handler
, public SceneMemberNotificationBus::Handler
{
public:
class LayoutDividerWidget
: public QGraphicsWidget
{
public:
AZ_CLASS_ALLOCATOR(LayoutDividerWidget, AZ::SystemAllocator, 0);
LayoutDividerWidget(QGraphicsItem* parent);
void UpdateStyle(const Styling::StyleHelper& styleHelper);
};
class LinearSlotGroupWidget
: public QGraphicsWidget
, public SlotUINotificationBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(LinearSlotGroupWidget, AZ::SystemAllocator, 0);
LinearSlotGroupWidget(QGraphicsItem* parent);
void DisplaySlot(const AZ::EntityId& slotId);
void RemoveSlot(const AZ::EntityId& slotId);
const AZStd::vector< SlotLayoutInfo >& GetInputSlots() const;
const AZStd::vector< SlotLayoutInfo >& GetOutputSlots() const;
bool IsEmpty() const;
void UpdateStyle(const Styling::StyleHelper& styleHelper);
// SlotUINotificationBus
void OnSlotLayoutPriorityChanged(int layoutPriority) override;
////
private:
int LayoutSlot(QGraphicsLinearLayout* layout, AZStd::vector<SlotLayoutInfo>& slotList, const SlotLayoutInfo& slotInfo);
QGraphicsLayoutItem* GetLayoutItem(const AZ::EntityId& slotId) const;
QGraphicsWidget* m_horizontalSpacer;
QGraphicsLinearLayout* m_inputs;
AZStd::vector< SlotLayoutInfo > m_inputSlots;
AZStd::unordered_set< SlotId > m_inputSlotSet;
QGraphicsLinearLayout* m_outputs;
AZStd::vector< SlotLayoutInfo > m_outputSlots;
AZStd::unordered_set< SlotId > m_outputSlotSet;
};
public:
AZ_TYPE_INFO(GeneralSlotLayoutGraphicsWidget, "{9DE7D3C0-D88C-47D8-85D4-5E0F619E60CB}");
AZ_CLASS_ALLOCATOR(GeneralSlotLayoutGraphicsWidget, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context) = delete;
GeneralSlotLayoutGraphicsWidget(GeneralSlotLayoutComponent& nodeSlots);
~GeneralSlotLayoutGraphicsWidget() override;
void Activate();
void Deactivate();
// NodeNotificationBus
void OnNodeActivated() override;
void OnSlotAddedToNode(const AZ::EntityId& slot) override;
void OnSlotRemovedFromNode(const AZ::EntityId& slot) override;
////
// NodeSlotsRequestBus
QGraphicsLayoutItem* GetGraphicsLayoutItem() override;
////
// SceneMemberNotificationBus
void OnSceneSet(const AZ::EntityId& sceneId);
////
// SlotLayoutRequestBus
void SetDividersEnabled(bool enabled) override;
void ConfigureSlotGroup(SlotGroup group, SlotGroupConfiguration configuration) override;
int GetSlotGroupDisplayOrder(SlotGroup group) const override;
bool IsSlotGroupVisible(SlotGroup group) const override;
void SetSlotGroupVisible(SlotGroup group, bool visible) override;
void ClearSlotGroup(SlotGroup group);
////
// StyleNotificationBus
void OnStyleChanged() override;
////
protected:
const AZ::EntityId& GetEntityId() const { return m_entityId; }
private:
bool DisplaySlot(const AZ::EntityId& slotId);
bool RemoveSlot(const AZ::EntityId& slotId);
void ActivateSlots();
void ClearLayout();
void UpdateLayout();
void UpdateStyles();
void RefreshDisplay();
LinearSlotGroupWidget* FindCreateSlotGroupWidget(SlotGroup slotType);
LayoutDividerWidget* FindCreateDividerWidget(int index);
GeneralSlotLayoutGraphicsWidget(const GeneralSlotLayoutComponent&) = delete;
GeneralSlotLayoutComponent& m_nodeSlots;
QGraphicsLinearLayout* m_groupLayout;
AZStd::unordered_map< SlotGroup, LinearSlotGroupWidget* > m_slotGroups;
AZStd::vector< LayoutDividerWidget* > m_dividers;
Styling::StyleHelper m_styleHelper;
AZ::EntityId m_entityId;
bool m_addedToScene;
};
}