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,636 @@
/*
* 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 <qtoolbutton.h>
#include <qscopedvaluerollback.h>
#include <QFocusEvent>
#include <QHeaderView>
#include <QWidgetAction>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Widgets/StyledItemDelegates/IconDecoratedNameDelegate.h>
#include <Editor/View/Dialogs/ContainerWizard/ContainerTypeLineEdit.h>
#include <Editor/View/Dialogs/ContainerWizard/ui_ContainerTypeLineEdit.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
namespace ScriptCanvasEditor
{
//////////////////////
// ContainerTypeMenu
//////////////////////
ContainerTypeMenu::ContainerTypeMenu(QWidget* parent)
: QDialog(parent, Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint)
, m_disableHiding(false)
, m_ignoreNextFocusIn(false)
{
setProperty("HasNoWindowDecorations", true);
setAttribute(Qt::WA_ShowWithoutActivating);
m_proxyModel.setSourceModel(GetModel());
m_proxyModel.sort(DataTypePaletteModel::ColumnIndex::Type);
m_tableView.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
m_tableView.setSelectionBehavior(QAbstractItemView::SelectRows);
m_tableView.setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
m_tableView.setItemDelegateForColumn(DataTypePaletteModel::Type, aznew GraphCanvas::IconDecoratedNameDelegate(this));
m_tableView.setModel(GetProxyModel());
m_tableView.verticalHeader()->hide();
m_tableView.horizontalHeader()->hide();
m_tableView.horizontalHeader()->setSectionResizeMode(DataTypePaletteModel::ColumnIndex::Pinned, QHeaderView::ResizeMode::ResizeToContents);
m_tableView.horizontalHeader()->setSectionResizeMode(DataTypePaletteModel::ColumnIndex::Type, QHeaderView::ResizeMode::Stretch);
m_tableView.installEventFilter(this);
m_tableView.setFocusPolicy(Qt::FocusPolicy::ClickFocus);
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(&m_tableView);
setLayout(layout);
m_disableHidingStateSetter.AddStateController(GetStateController());
QObject::connect(&m_tableView, &QTableView::clicked, this, &ContainerTypeMenu::OnTableClicked);
}
ContainerTypeMenu::~ContainerTypeMenu()
{
}
DataTypePaletteModel* ContainerTypeMenu::GetModel()
{
return &m_model;
}
const DataTypePaletteModel* ContainerTypeMenu::GetModel() const
{
return &m_model;
}
DataTypePaletteSortFilterProxyModel* ContainerTypeMenu::GetProxyModel()
{
return &m_proxyModel;
}
const DataTypePaletteSortFilterProxyModel* ContainerTypeMenu::GetProxyModel() const
{
return &m_proxyModel;
}
void ContainerTypeMenu::ShowMenu()
{
clearFocus();
m_tableView.clearFocus();
show();
m_disableHidingStateSetter.ReleaseState();
}
void ContainerTypeMenu::HideMenu()
{
m_disableHidingStateSetter.ReleaseState();
m_tableView.clearFocus();
clearFocus();
reject();
}
void ContainerTypeMenu::reject()
{
if (!m_disableHiding.GetState())
{
QDialog::reject();
}
}
bool ContainerTypeMenu::eventFilter(QObject* object, QEvent* event)
{
if (object == &m_tableView)
{
if (event->type() == QEvent::FocusOut)
{
HandleFocusOut();
}
else if (event->type() == QEvent::FocusIn)
{
HandleFocusIn();
}
}
return false;
}
void ContainerTypeMenu::focusInEvent(QFocusEvent* focusEvent)
{
QDialog::focusInEvent(focusEvent);
if (focusEvent->isAccepted())
{
if (!m_ignoreNextFocusIn)
{
HandleFocusIn();
}
else
{
m_ignoreNextFocusIn = false;
}
}
}
void ContainerTypeMenu::focusOutEvent(QFocusEvent* focusEvent)
{
QDialog::focusOutEvent(focusEvent);
HandleFocusOut();
}
void ContainerTypeMenu::showEvent(QShowEvent* showEvent)
{
QDialog::showEvent(showEvent);
// So, despite me telling it to not activate, the window still gets a focus in event.
// But, it doesn't get a foccus out event, since it doesn't actually accept the focus in event?
m_ignoreNextFocusIn = true;
m_tableView.selectionModel()->clearSelection();
Q_EMIT VisibilityChanged(true);
}
void ContainerTypeMenu::hideEvent(QHideEvent* hideEvent)
{
QDialog::hideEvent(hideEvent);
clearFocus();
Q_EMIT VisibilityChanged(false);
m_tableView.selectionModel()->clearSelection();
}
GraphCanvas::StateController<bool>* ContainerTypeMenu::GetStateController()
{
return &m_disableHiding;
}
void ContainerTypeMenu::SetSelectedRow(int row)
{
m_tableView.selectionModel()->clear();
if (row <= m_proxyModel.rowCount()
&& row >= 0)
{
QItemSelection rowSelection(m_proxyModel.index(row, 0), m_proxyModel.index(row, m_proxyModel.columnCount()-1));
m_tableView.selectionModel()->select(rowSelection, QItemSelectionModel::Select);
m_tableView.scrollTo(m_proxyModel.index(row, 0));
}
}
int ContainerTypeMenu::GetSelectedRow() const
{
if (m_tableView.selectionModel()->hasSelection())
{
QModelIndexList selectedIndexes = m_tableView.selectionModel()->selectedIndexes();
if (!selectedIndexes.empty())
{
return selectedIndexes.front().row();
}
}
return -1;
}
AZ::TypeId ContainerTypeMenu::GetSelectedTypeId() const
{
QModelIndexList selectedIndexes = m_tableView.selectionModel()->selectedIndexes();
if (!selectedIndexes.empty())
{
QModelIndex firstSelection = selectedIndexes.front();
QModelIndex sourceIndex = m_proxyModel.mapToSource(firstSelection);
return m_model.FindTypeIdForIndex(sourceIndex);
}
return AZ::TypeId::CreateNull();
}
void ContainerTypeMenu::OnTableClicked(const QModelIndex& modelIndex)
{
if (modelIndex.isValid())
{
QModelIndex sourceIndex = m_proxyModel.mapToSource(modelIndex);
AZ::TypeId typeId = m_model.FindTypeIdForIndex(sourceIndex);
if (!typeId.IsNull())
{
Q_EMIT ContainerTypeSelected(typeId);
QTimer::singleShot(0, [this]() { accept(); });
}
}
}
void ContainerTypeMenu::HandleFocusIn()
{
m_disableHidingStateSetter.SetState(true);
}
void ContainerTypeMenu::HandleFocusOut()
{
m_disableHidingStateSetter.ReleaseState();
QTimer::singleShot(0, [this]() { reject(); });
}
//////////////////////////
// ContainerTypeLineEdit
//////////////////////////
ContainerTypeLineEdit::ContainerTypeLineEdit(int index, QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::ContainerTypeLineEdit())
, m_ignoreNextComplete(false)
, m_recursionBlocker(false)
, m_index(index)
, m_lastId(azrtti_typeid<void>())
, m_dataTypeMenu(parent)
{
m_ui->setupUi(this);
QAction* action = m_ui->variableType->addAction(QIcon(":/ScriptCanvasEditorResources/Resources/triangle.png"), QLineEdit::ActionPosition::TrailingPosition);
QObject::connect(action, &QAction::triggered, this, &ContainerTypeLineEdit::OnOptionsClicked);
m_completer.setModel(m_dataTypeMenu.GetModel());
m_completer.setCompletionColumn(DataTypePaletteModel::ColumnIndex::Type);
m_completer.setCompletionMode(QCompleter::CompletionMode::InlineCompletion);
m_completer.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
m_ui->variableType->installEventFilter(this);
m_ui->variableType->setCompleter(&m_completer);
QObject::connect(m_ui->variableType, &QLineEdit::textEdited, this, &ContainerTypeLineEdit::OnTextChanged);
QObject::connect(m_ui->variableType, &QLineEdit::returnPressed, this, &ContainerTypeLineEdit::OnReturnPressed);
QObject::connect(m_ui->variableType, &QLineEdit::editingFinished, this, &ContainerTypeLineEdit::OnEditComplete);
m_filterTimer.setInterval(500);
QObject::connect(&m_filterTimer, &QTimer::timeout, this, &ContainerTypeLineEdit::UpdateFilter);
QObject::connect(&m_dataTypeMenu, &ContainerTypeMenu::ContainerTypeSelected, this, &ContainerTypeLineEdit::SelectType);
QObject::connect(&m_dataTypeMenu, &ContainerTypeMenu::VisibilityChanged, this, &ContainerTypeLineEdit::DataTypeMenuVisibilityChanged);
m_dataTypeMenu.accept();
m_disableHidingStateSetter.AddStateController(m_dataTypeMenu.GetStateController());
}
ContainerTypeLineEdit::~ContainerTypeLineEdit()
{
m_dataTypeMenu.accept();
}
void ContainerTypeLineEdit::SetDisplayName(AZStd::string_view name)
{
m_ui->nameDisplay->setText(name.data());
}
void ContainerTypeLineEdit::SetDataTypes(const AZStd::unordered_set< AZ::TypeId >& dataTypes)
{
m_dataTypeMenu.GetModel()->ClearTypes();
m_dataTypeMenu.GetModel()->PopulateVariablePalette(dataTypes);
}
AZ::TypeId ContainerTypeLineEdit::GetDefaultTypeId() const
{
const DataTypePaletteSortFilterProxyModel* proxyModel = m_dataTypeMenu.GetProxyModel();
if (proxyModel->rowCount() > 0)
{
QModelIndex index = proxyModel->index(0, 0);
QModelIndex sourceIndex = proxyModel->mapToSource(index);
const DataTypePaletteModel* paletteModel = m_dataTypeMenu.GetModel();
return paletteModel->FindTypeIdForIndex(sourceIndex);
}
return azrtti_typeid<void>();
}
void ContainerTypeLineEdit::SelectType(const AZ::TypeId& typeId)
{
if (DisplayType(typeId))
{
Q_EMIT TypeChanged(m_index, typeId);
}
}
bool ContainerTypeLineEdit::DisplayType(const AZ::TypeId& typeId)
{
QSignalBlocker signalBlocker(m_ui->variableType);
AZStd::string typeName = m_dataTypeMenu.GetModel()->FindTypeNameForTypeId(typeId);
if (!typeName.empty())
{
m_completer.setCompletionPrefix(typeName.c_str());
m_ui->variableType->setText(typeName.c_str());
m_lastId = typeId;
}
else
{
m_completer.setCompletionPrefix("");
m_ui->variableType->setText("");
m_lastId = azrtti_typeid<void>();
}
// Clear out any selection since this might be coming from an auto complete
m_ui->variableType->setSelection(0, 0);
const QPixmap* pixmapIcon = nullptr;
GraphCanvas::StyleManagerRequestBus::EventResult(pixmapIcon, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetDataTypeIcon, m_lastId);
m_ui->iconLabel->setPixmap((*pixmapIcon));
return !typeName.empty();
}
QWidget* ContainerTypeLineEdit::GetLineEdit() const
{
return m_ui->variableType;
}
void ContainerTypeLineEdit::ResetLineEdit()
{
m_disableHidingStateSetter.ReleaseState();
m_lastId = azrtti_typeid<void>();
m_completer.setCompletionPrefix("");
m_dataTypeMenu.GetProxyModel()->SetFilter("");
HideDataTypeMenu();
}
void ContainerTypeLineEdit::CancelDataInput()
{
DisplayType(m_lastId);
HideDataTypeMenu();
}
void ContainerTypeLineEdit::HideDataTypeMenu()
{
m_dataTypeMenu.HideMenu();
}
bool ContainerTypeLineEdit::eventFilter(QObject* obj, QEvent* event)
{
if (obj == m_ui->variableType)
{
switch (event->type())
{
case QEvent::FocusOut:
{
m_disableHidingStateSetter.ReleaseState();
QTimer::singleShot(0, [this]() { this->m_dataTypeMenu.reject(); });
break;
}
case QEvent::KeyPress:
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Down)
{
if (m_dataTypeMenu.isHidden())
{
m_dataTypeMenu.GetProxyModel()->SetFilter("");
DisplayMenu();
}
int selectedIndex = m_dataTypeMenu.GetSelectedRow();
if (selectedIndex < 0)
{
selectedIndex = 0;
}
else
{
selectedIndex += 1;
if (selectedIndex >= m_dataTypeMenu.GetProxyModel()->rowCount())
{
selectedIndex = 0;
}
}
m_dataTypeMenu.SetSelectedRow(selectedIndex);
AZ::TypeId typeId = m_dataTypeMenu.GetSelectedTypeId();
AZStd::string typeName = m_dataTypeMenu.GetModel()->FindTypeNameForTypeId(typeId);
if (!typeName.empty() && !typeId.IsNull())
{
m_ui->variableType->setText(typeName.c_str());
m_ui->variableType->setSelection(0, static_cast<int>(typeName.size()));
m_completer.setCompletionPrefix(typeName.c_str());
}
return true;
}
else if (keyEvent->key() == Qt::Key_Up)
{
if (m_dataTypeMenu.isHidden())
{
m_dataTypeMenu.GetProxyModel()->SetFilter("");
DisplayMenu();
}
int selectedIndex = m_dataTypeMenu.GetSelectedRow();
if (selectedIndex < 0)
{
selectedIndex = m_dataTypeMenu.GetProxyModel()->rowCount() - 1;
}
else
{
selectedIndex -= 1;
if (selectedIndex < 0)
{
selectedIndex = m_dataTypeMenu.GetProxyModel()->rowCount() - 1;
}
}
m_dataTypeMenu.SetSelectedRow(selectedIndex);
AZ::TypeId typeId = m_dataTypeMenu.GetSelectedTypeId();
AZStd::string typeName = m_dataTypeMenu.GetModel()->FindTypeNameForTypeId(typeId);
if (!typeName.empty() && !typeId.IsNull())
{
m_ui->variableType->setText(typeName.c_str());
m_ui->variableType->setSelection(0, static_cast<int>(typeName.size()));
m_completer.setCompletionPrefix(typeName.c_str());
}
return true;
}
else if (keyEvent->key() == Qt::Key_Escape)
{
DisplayType(m_lastId);
}
}
default:
break;
}
}
return false;
}
void ContainerTypeLineEdit::OnTextChanged()
{
DisplayMenu();
UpdateFilter();
}
void ContainerTypeLineEdit::OnOptionsClicked()
{
if (m_dataTypeMenu.isHidden())
{
m_dataTypeMenu.GetProxyModel()->SetFilter("");
DisplayMenu();
}
else
{
m_dataTypeMenu.accept();
}
}
void ContainerTypeLineEdit::OnReturnPressed()
{
const bool allowReset = false;
if (SubmitData(allowReset))
{
m_dataTypeMenu.accept();
}
else
{
m_ui->variableType->setText("");
UpdateFilter();
}
// When we press enter, we will also get an editing complete signal. We want to ignore that since we handled it here.
m_ignoreNextComplete = true;
}
void ContainerTypeLineEdit::OnEditComplete()
{
if (m_ignoreNextComplete)
{
m_ignoreNextComplete = false;
return;
}
SubmitData();
QTimer::singleShot(0, [this]() { m_dataTypeMenu.reject(); });
}
void ContainerTypeLineEdit::UpdateFilter()
{
m_dataTypeMenu.GetProxyModel()->SetFilter(GetUserInputText());
}
bool ContainerTypeLineEdit::SubmitData(bool allowReset)
{
AZStd::string typeName = m_ui->variableType->text().toUtf8().data();
AZ::TypeId typeId = m_dataTypeMenu.GetModel()->FindTypeIdForTypeName(typeName);
// We didn't input a valid type. So default to our last previously known value.
if (typeId == azrtti_typeid<void>())
{
if (allowReset)
{
DisplayType(m_lastId);
typeId = m_lastId;
}
}
else if (typeId != m_lastId)
{
SelectType(typeId);
}
return typeId != azrtti_typeid<void>();
}
void ContainerTypeLineEdit::DisplayMenu()
{
if (!m_recursionBlocker)
{
QScopedValueRollback<bool> valueRollback(m_recursionBlocker, true);
if (m_dataTypeMenu.isHidden())
{
m_dataTypeMenu.ShowMenu();
QRect dialogGeometry = m_dataTypeMenu.geometry();
dialogGeometry.moveTopLeft(m_ui->variableType->mapToGlobal(QPoint(0, m_ui->variableType->height())));
dialogGeometry.setWidth(m_ui->variableType->width());
m_dataTypeMenu.setGeometry(dialogGeometry);
}
}
if (!m_disableHidingStateSetter.HasState())
{
m_disableHidingStateSetter.SetState(true);
}
}
QString ContainerTypeLineEdit::GetUserInputText()
{
QString lineEditText = m_ui->variableType->text();
// The QCompleter doesn't seem to update the completion prefix when you delete anything, only when things are added.
// To get it to update correctly when the user deletes something, I'm using the combination of things:
//
// 1) If we have a completion, that text will be auto filled into the quick filter because of the completion model.
// So, we will compare those two values, and if they match, we know we want to search using the completion prefix.
//
// 2) If they don't match, it means that user deleted something, and the Completer didn't update it's internal state, so we'll just
// use whatever is in the text box.
//
// 3) When the text field is set to empty, the current completion gets invalidated, but the prefix doesn't, so that gets special cased out.
//
// Extra fun: If you type in something, "Like" then delete a middle character, "Lie", and then put the k back in. It will auto complete the E
// visually but the completion prefix will be the entire word.
if (m_ui->variableType->completer()
&& m_ui->variableType->completer()->currentCompletion().compare(lineEditText, Qt::CaseInsensitive) == 0
&& !lineEditText.isEmpty())
{
lineEditText = m_ui->variableType->completer()->completionPrefix();
}
return lineEditText;
}
}
#include <Editor/View/Dialogs/ContainerWizard/moc_ContainerTypeLineEdit.cpp>
@@ -0,0 +1,156 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QCompleter>
#include <QDialog>
#include <QMenu>
#include <QTableView>
#include <qtimer.h>
#include <qpixmap.h>
#include <QWidget>
#include <AzCore/Memory/SystemAllocator.h>
#include <Editor/View/Widgets/DataTypePalette/DataTypePaletteModel.h>
#include <GraphCanvas/Utils/StateControllers/StackStateController.h>
#endif
namespace Ui
{
class ContainerTypeLineEdit;
class ContainerTypeComboBox;
}
namespace ScriptCanvasEditor
{
class ContainerTypeMenu
: public QDialog
{
Q_OBJECT
public:
ContainerTypeMenu(QWidget* parent = nullptr);
~ContainerTypeMenu() override;
DataTypePaletteModel* GetModel();
const DataTypePaletteModel* GetModel() const;
DataTypePaletteSortFilterProxyModel* GetProxyModel();
const DataTypePaletteSortFilterProxyModel* GetProxyModel() const;
void ShowMenu();
void HideMenu();
void reject() override;
bool eventFilter(QObject* object, QEvent* event) override;
void focusInEvent(QFocusEvent* focusEvent) override;
void focusOutEvent(QFocusEvent* focusEvent) override;
void showEvent(QShowEvent* showEvent) override;
void hideEvent(QHideEvent* hideEvent) override;
GraphCanvas::StateController<bool>* GetStateController();
void SetSelectedRow(int row);
int GetSelectedRow() const;
AZ::TypeId GetSelectedTypeId() const;
void OnTableClicked(const QModelIndex& modelIndex);
Q_SIGNALS:
void ContainerTypeSelected(const AZ::TypeId& typeId);
void VisibilityChanged(bool visibility);
private:
void HandleFocusIn();
void HandleFocusOut();
QTableView m_tableView;
DataTypePaletteSortFilterProxyModel m_proxyModel;
DataTypePaletteModel m_model;
GraphCanvas::StateSetter<bool> m_disableHidingStateSetter;
GraphCanvas::StackStateController<bool> m_disableHiding;
bool m_ignoreNextFocusIn;
};
class ContainerTypeLineEdit
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ContainerTypeLineEdit, AZ::SystemAllocator, 0);
ContainerTypeLineEdit(int index, QWidget* parent = nullptr);
~ContainerTypeLineEdit();
void SetDisplayName(AZStd::string_view name);
void SetDataTypes(const AZStd::unordered_set< AZ::TypeId >& dataTypes);
AZ::TypeId GetDefaultTypeId() const;
void SelectType(const AZ::TypeId& typeId);
bool DisplayType(const AZ::TypeId& typeId);
QWidget* GetLineEdit() const;
void ResetLineEdit();
void CancelDataInput();
void HideDataTypeMenu();
Q_SIGNALS:
void TypeChanged(int index, const AZ::TypeId& typeId);
void DataTypeMenuVisibilityChanged(bool visible);
protected:
bool eventFilter(QObject* obj, QEvent* event);
void OnTextChanged();
void OnOptionsClicked();
void OnReturnPressed();
void OnEditComplete();
void UpdateFilter();
private:
bool SubmitData(bool allowReset = true);
void DisplayMenu();
QString GetUserInputText();
AZStd::unique_ptr<Ui::ContainerTypeLineEdit> m_ui;
QTimer m_filterTimer;
bool m_ignoreNextComplete;
bool m_recursionBlocker;
int m_index;
AZ::TypeId m_lastId;
const QPixmap* m_iconPixmap;
QCompleter m_completer;
ContainerTypeMenu m_dataTypeMenu;
GraphCanvas::StateSetter<bool> m_disableHidingStateSetter;
};
}
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ContainerTypeLineEdit</class>
<widget class="QWidget" name="ContainerTypeLineEdit">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>462</width>
<height>368</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="nameDisplay">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>115</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Specifies the type of Container to Create.</string>
</property>
<property name="text">
<string>Key Type</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="iconLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="variableType"/>
</item>
</layout>
</widget>
<resources>
<include location="../../Windows/ScriptCanvasEditorResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,622 @@
/*
* 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 <AzCore/Serialization/Utils.h>
#include <AzCore/ScriptCanvas/ScriptCanvasOnDemandNames.h>
#include <Editor/View/Dialogs/ContainerWizard/ContainerWizard.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <Editor/View/Dialogs/ContainerWizard/ui_ContainerWizard.h>
AZ_POP_DISABLE_WARNING
#include <Editor/Include/ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <Editor/View/Dialogs/ContainerWizard/ContainerTypeLineEdit.h>
#include <Editor/View/Widgets/VariablePanel/VariableDockWidget.h>
#include <Editor/Settings.h>
#include <Editor/Translation/TranslationHelper.h>
#include <ScriptCanvas/Data/Data.h>
#include <ScriptCanvas/Variable/VariableBus.h>
namespace ScriptCanvasEditor
{
////////////////////
// ContainerWizard
////////////////////
ContainerWizard::ContainerWizard(QWidget* parent)
: QDialog(parent, Qt::FramelessWindowHint)
, m_serializeContext(nullptr)
, m_validationAction(nullptr)
, m_invalidIcon(":/ScriptCanvasEditorResources/Resources/error_icon.png")
, m_dataTypeMenuVisibile(false)
, m_releaseVariable(false)
, m_variableCounter(0)
, m_ui(new Ui::ContainerWizard())
{
m_ui->setupUi(this);
QObject::connect(m_ui->containerTypeBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ContainerWizard::OnContainerTypeChanged);
m_ui->containerTypeBox->setEditable(false);
// Don't want to enable enter triggering through the default mechanism for the create/cancel, since it causes a bunch of accidental triggers
// of submission while editing. Instead install an event filter and deal with this internally.
m_ui->createButton->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
m_ui->createButton->setAutoDefault(false);
m_ui->createButton->setDefault(false);
m_ui->createButton->installEventFilter(this);
m_ui->cancelButton->setFocusPolicy(Qt::FocusPolicy::StrongFocus);
m_ui->cancelButton->setAutoDefault(false);
m_ui->cancelButton->setDefault(false);
m_ui->cancelButton->installEventFilter(this);
QObject::connect(m_ui->createButton, &QPushButton::clicked, this, &ContainerWizard::OnCreate);
QObject::connect(m_ui->cancelButton, &QPushButton::clicked, this, &ContainerWizard::OnCancel);
QObject::connect(this, &QDialog::finished, this, &ContainerWizard::OnFinished);
QObject::connect(m_ui->variableName, &QLineEdit::textChanged, this, &ContainerWizard::ValidateName);
}
ContainerWizard::~ContainerWizard()
{
ClearDisplay();
for (ContainerTypeLineEdit* lineEdit : m_containerTypeLineEdit)
{
delete lineEdit;
}
}
void ContainerWizard::SetActiveScriptCanvasId(const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
{
m_activeScriptCanvasId = scriptCanvasId;
}
void ContainerWizard::RegisterType(const AZ::TypeId& dataType)
{
if (AZ::Utils::IsContainerType(dataType))
{
RegisterContainerType(dataType);
}
else
{
RegisterDataType(dataType);
}
}
void ContainerWizard::ShowWizard(const AZ::TypeId& genericContainerType)
{
// Always default the wizard to the unchecked state
m_ui->checkBox->setChecked(false);
if (m_ui->containerTypeBox->count() != m_genericContainerTypes.size())
{
QSignalBlocker signalBlock(m_ui->containerTypeBox);
std::sort(m_genericContainerTypeNames.begin(), m_genericContainerTypeNames.end(), [](const AZStd::pair< AZStd::string, AZ::TypeId>& lhs,
const AZStd::pair< AZStd::string, AZ::TypeId>& rhs)
{
return AZStd::less<AZStd::string>()(lhs.first, rhs.first);
});
m_ui->containerTypeBox->clear();
for (const auto& element : m_genericContainerTypeNames)
{
m_ui->containerTypeBox->addItem(element.first.c_str());
}
}
for (int i = 0; i < m_genericContainerTypeNames.size(); ++i)
{
const auto& element = m_genericContainerTypeNames[i];
if (element.second == genericContainerType)
{
QSignalBlocker signalBlock(m_ui->containerTypeBox);
m_ui->containerTypeBox->setCurrentIndex(i);
}
}
// Need to show before trying to initialize the display otherwise the line edits won't be cleaned up
// correctly.
show();
InitializeDisplay(genericContainerType);
m_releaseVariable = true;
bool nameAvailable = false;
AZStd::string variableName;
do
{
SceneCounterRequestBus::EventResult(m_variableCounter, m_activeScriptCanvasId, &SceneCounterRequests::GetNewVariableCounter);
variableName = VariableDockWidget::ConstructDefaultVariableName(m_variableCounter);
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(nameAvailable, m_activeScriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::IsNameAvailable, variableName);
} while (!nameAvailable);
m_ui->variableName->setText(variableName.c_str());
m_ui->variableName->setFocus(Qt::FocusReason::MouseFocusReason);
m_ui->variableName->setSelection(0, m_ui->variableName->text().size());
}
void ContainerWizard::accept()
{
QDialog::accept();
}
void ContainerWizard::reject()
{
if (m_dataTypeMenuVisibile)
{
for (ContainerTypeLineEdit* lineEdit : m_containerTypeLineEdit)
{
lineEdit->HideDataTypeMenu();
}
m_dataTypeMenuVisibile = false;
}
else
{
QDialog::reject();
}
}
void ContainerWizard::hideEvent(QHideEvent* hideEvent)
{
QDialog::hideEvent(hideEvent);
for (ContainerTypeLineEdit* lineEdit : m_containerTypeLineEdit)
{
lineEdit->HideDataTypeMenu();
}
m_dataTypeMenuVisibile = false;
}
bool ContainerWizard::eventFilter(QObject* object, QEvent* event)
{
if (object == m_ui->createButton
|| object == m_ui->cancelButton)
{
if (event->type() == QEvent::Type::KeyRelease)
{
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Enter
|| keyEvent->key() == Qt::Key_Return)
{
QPushButton* button = qobject_cast<QPushButton*>(object);
if (button)
{
button->click();
}
}
}
}
return false;
}
const AZStd::unordered_map<AZ::Crc32, AZ::TypeId>& ContainerWizard::GetFinalTypeMapping() const
{
return m_finalContainerTypeIds;
}
void ContainerWizard::ReparseDisplay()
{
AZ::Crc32 workingCrc = AZ::Crc32(m_genericType.ToString<AZStd::string>().c_str());
for (int typeIndex = 0; typeIndex < m_containerTypes.size(); ++typeIndex)
{
const AZ::TypeId& typeId = m_containerTypes[typeIndex];
ContainerTypeLineEdit* lineEdit = GetLineEdit(typeIndex);
auto dataTypeIter = m_containerDataTypeSets.find(workingCrc);
if (dataTypeIter == m_containerDataTypeSets.end())
{
// No idea wtf do here we've managed to put ourselves into an invalid state
AZ_Error("ScriptCanvas", false, "Unknown partial type found in Container Creation. Aborting.");
close();
break;
}
lineEdit->SetDataTypes(dataTypeIter->second);
AZ::TypeId selectedTypeId = typeId;
{
QSignalBlocker signalBlocker(lineEdit);
if (!lineEdit->DisplayType(typeId))
{
selectedTypeId = lineEdit->GetDefaultTypeId();
lineEdit->DisplayType(typeId);
}
}
m_containerTypes[typeIndex] = selectedTypeId;
workingCrc.Add(selectedTypeId.ToString<AZStd::string>().c_str());
}
}
void ContainerWizard::OnCreate()
{
AZ::Crc32 typeCrc = AZ::Crc32(m_genericType.ToString<AZStd::string>().c_str());
for (const AZ::TypeId& typeId : m_containerTypes)
{
typeCrc.Add(typeId.ToString<AZStd::string>().c_str());
}
auto containerIter = m_finalContainerTypeIds.find(typeCrc);
if (containerIter != m_finalContainerTypeIds.end())
{
m_releaseVariable = false;
AZStd::string variableName = m_ui->variableName->text().toUtf8().data();
if (m_ui->checkBox->isChecked())
{
AZStd::intrusive_ptr<EditorSettings::ScriptCanvasEditorSettings> settings = AZ::UserSettings::CreateFind<EditorSettings::ScriptCanvasEditorSettings>(AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL);
if (settings)
{
auto insertResult = settings->m_pinnedDataTypes.insert(containerIter->second);
if (insertResult.second)
{
Q_EMIT ContainerPinned(containerIter->second);
}
}
}
Q_EMIT CreateContainerVariable(variableName, containerIter->second);
close();
}
else
{
AZ_Warning("ScriptCanvas", false, "Unable to find Registered type with the given parameters.");
}
}
void ContainerWizard::OnCancel()
{
close();
}
void ContainerWizard::OnFinished([[maybe_unused]] int result)
{
ClearDisplay();
if (m_releaseVariable)
{
SceneCounterRequestBus::Event(m_activeScriptCanvasId, &SceneCounterRequests::ReleaseVariableCounter, m_variableCounter);
}
}
void ContainerWizard::OnContainerTypeChanged(int index)
{
if (index >= 0 && index < m_genericContainerTypeNames.size())
{
InitializeDisplay(m_genericContainerTypeNames[index].second);
}
}
void ContainerWizard::OnTypeChanged(int index, const AZ::TypeId& typeId)
{
if (index >= 0 && index < m_containerTypes.size())
{
m_containerTypes[index] = typeId;
ReparseDisplay();
}
}
void ContainerWizard::OnDataTypeMenuVisibilityChanged(bool visible)
{
m_dataTypeMenuVisibile = visible;
}
void ContainerWizard::ValidateName(const QString& newName)
{
if (m_validationAction)
{
m_ui->variableName->removeAction(m_validationAction);
delete m_validationAction;
m_validationAction = nullptr;
}
ScriptCanvas::VariableValidationOutcome validName = AZ::Failure(ScriptCanvas::GraphVariableValidationErrorCode::Unknown);
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(validName, m_activeScriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::IsNameValid, newName.toUtf8().data());
if (!validName || newName.isEmpty())
{
m_validationAction = m_ui->variableName->addAction(m_invalidIcon, QLineEdit::TrailingPosition);
if (validName.GetError() == ScriptCanvas::GraphVariableValidationErrorCode::Invalid)
{
m_validationAction->setToolTip("A Variable name cannot be empty or over 200 characters.\nPlease specify a new name for the variable.");
}
else if (validName.GetError() == ScriptCanvas::GraphVariableValidationErrorCode::Duplicate)
{
m_validationAction->setToolTip("This name is already in use by\nanother variable");
}
}
m_ui->createButton->setEnabled((validName && !newName.isEmpty()));
}
void ContainerWizard::ClearDisplay()
{
m_containerTypes.clear();
while (m_ui->typeSelectionFrame->layout()->count() > 0)
{
QLayoutItem* layoutItem = m_ui->typeSelectionFrame->layout()->takeAt(0);
}
for (ContainerTypeLineEdit* lineEdit : m_containerTypeLineEdit)
{
if (lineEdit->isVisible())
{
lineEdit->ResetLineEdit();
lineEdit->setVisible(false);
}
else
{
break;
}
}
}
void ContainerWizard::InitializeDisplay(const AZ::TypeId& typeId)
{
m_genericType = typeId;
if (AZ::Utils::IsMapContainerType(m_genericType))
{
PopulateMapDisplay();
}
else
{
PopulateGeneralDisplay();
}
adjustSize();
}
void ContainerWizard::PopulateMapDisplay()
{
const AZStd::vector< AZStd::string > typeLabels = { "Key", "Value" };
PopulateGeneralDisplay("Map %i", "Map", typeLabels);
}
void ContainerWizard::PopulateGeneralDisplay(const AZStd::string& patternFallback, const AZStd::string& singleTypeString, const AZStd::vector< AZStd::string >& typeLabels)
{
ClearDisplay();
AZ::Crc32 workingCrc = AZ::Crc32(m_genericType.ToString<AZStd::string>().c_str());
int containerIndex = 0;
auto dataTypeIter = m_containerDataTypeSets.find(workingCrc);
QWidget* focusWidget = m_ui->variableName;
while (dataTypeIter != m_containerDataTypeSets.end())
{
DataTypeSet& dataTypeSet = dataTypeIter->second;
ContainerTypeLineEdit* lineEdit = GetLineEdit(containerIndex);
lineEdit->ResetLineEdit();
if (containerIndex >= typeLabels.size())
{
AZStd::string containerName = AZStd::string::format(patternFallback.c_str(), containerIndex);
lineEdit->SetDisplayName(containerName);
}
else
{
lineEdit->SetDisplayName(typeLabels[containerIndex]);
}
lineEdit->SetDataTypes(dataTypeSet);
lineEdit->setVisible(true);
m_ui->typeSelectionFrame->layout()->addWidget(lineEdit);
AZ::TypeId typeId = lineEdit->GetDefaultTypeId();
workingCrc.Add(typeId.ToString<AZStd::string>().c_str());
m_containerTypes.emplace_back(typeId);
{
QSignalBlocker signalBlocker(lineEdit);
lineEdit->DisplayType(typeId);
}
QWidget* nextFocus = lineEdit->GetLineEdit();
setTabOrder(focusWidget, nextFocus);
focusWidget = nextFocus;
++containerIndex;
dataTypeIter = m_containerDataTypeSets.find(workingCrc);
}
setTabOrder(focusWidget, m_ui->createButton);
if (containerIndex == 1)
{
ContainerTypeLineEdit* lineEdit = GetLineEdit(0);
lineEdit->SetDisplayName(singleTypeString);
}
}
void ContainerWizard::RegisterDataType(const AZ::TypeId& dataType)
{
m_dataTypes[dataType] = ScriptCanvasEditor::TranslationHelper::GetSafeTypeName(ScriptCanvas::Data::FromAZType(dataType));
}
void ContainerWizard::RegisterContainerType(const AZ::TypeId& containerType)
{
if (m_serializeContext == nullptr)
{
AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
if (m_serializeContext == nullptr)
{
AZ_Warning("ScriptCanvas", false, "Not given a SerializeContext and unable to find a SerializeContext to deduce generic ContainerTypes from.");
}
}
AZStd::vector<AZ::Uuid> containedTypes = AZ::Utils::GetContainedTypes(containerType);
AZ::GenericClassInfo* classInfo = m_serializeContext->FindGenericClassInfo(containerType);
if (classInfo)
{
// Until we get the ability to create generic versions of these containers
// Keep track of all of the partial matches so we can generate various lists in order to pretend
// that we have general Key/Value support.
//
// Using the Partial CRC to identify a partial match so we can sort of soft fill out our template by
// knowing all of the possible outcomes given Container X with first element Y
AZ::Crc32 workingCrc = AZ::Crc32(classInfo->GetGenericTypeId().ToString<AZStd::string>().c_str());
for (const AZ::Uuid& containedType : containedTypes)
{
DataTypeSet& dataTypeSet = m_containerDataTypeSets[workingCrc];
if (ScriptCanvas::Data::IsNumber(containedType))
{
dataTypeSet.insert(azrtti_typeid<ScriptCanvas::Data::NumberType>());
workingCrc.Add(containedType.ToString<AZStd::string>().c_str());
}
else
{
dataTypeSet.insert(containedType);
workingCrc.Add(containedType.ToString<AZStd::string>().c_str());
}
}
if (m_finalContainerTypeIds.find(workingCrc) == m_finalContainerTypeIds.end())
{
m_finalContainerTypeIds[workingCrc] = containerType;
}
// Need to populate the combo box
AZ::TypeId genericTypeId = AZ::Utils::GetGenericContainerType(containerType);
auto insertResult = m_genericContainerTypes.insert(genericTypeId);
if (insertResult.second)
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
if (behaviorContext)
{
auto mapIter = behaviorContext->m_typeToClassMap.find(containerType);
if (mapIter != behaviorContext->m_typeToClassMap.end())
{
AZ::Attribute* categoryAttribute = AZ::FindAttribute(AZ::Script::Attributes::Category, mapIter->second->m_attributes);
AZStd::string categoryName;
if (categoryAttribute)
{
if (AZ::AttributeReader(nullptr, categoryAttribute).Read<AZStd::string>(categoryName, *behaviorContext))
{
m_genericContainerTypeNames.emplace_back(categoryName, genericTypeId);
}
else
{
m_genericContainerTypes.erase(genericTypeId);
}
}
AZ::Attribute* tooltipAttribute = AZ::FindAttribute(AZ::Script::Attributes::ToolTip, mapIter->second->m_attributes);
if (tooltipAttribute)
{
AZStd::string containerToolTip;
if (AZ::AttributeReader(nullptr, tooltipAttribute).Read<AZStd::string>(containerToolTip, *behaviorContext))
{
QString toolTip = m_ui->containerLabel->toolTip();
// We want to sort the container tool tips so the order they are added matches the order in which
// we display them in the combobox.
//
// I don't really have a final point at which to resolve this currently, so I'm just going to sort the elements on the
// fly with a ton of string manipulation to get it to align correctly.
QStringList previousToolTips = toolTip.split("\n");
if (!previousToolTips.empty())
{
toolTip.clear();
// First tooltip is always the native tool tip. So we want to maintain that
toolTip.append(previousToolTips[0]);
previousToolTips.removeFirst();
QString newToolTip = QString(" %1 - %2").arg(categoryName.c_str()).arg(containerToolTip.c_str());
previousToolTips.push_back(newToolTip);
previousToolTips.sort(Qt::CaseInsensitive);
for (const QString& toolTipString : previousToolTips)
{
toolTip.append("\n");
toolTip.append(toolTipString);
}
m_ui->containerLabel->setToolTip(toolTip);
}
}
}
}
}
}
}
else
{
AZ_Warning("ScriptCanvas", false, "Could not find generic class info for container with TypeId(%s)", containerType.ToString<AZStd::string>().c_str());
}
}
ContainerTypeLineEdit* ContainerWizard::GetLineEdit(int typeIndex)
{
while (m_containerTypeLineEdit.size() <= typeIndex)
{
ContainerTypeLineEdit* lineEdit = aznew ContainerTypeLineEdit(typeIndex, this);
QObject::connect(lineEdit, &ContainerTypeLineEdit::TypeChanged, this, &ContainerWizard::OnTypeChanged);
QObject::connect(lineEdit, &ContainerTypeLineEdit::DataTypeMenuVisibilityChanged, this, &ContainerWizard::OnDataTypeMenuVisibilityChanged);
m_containerTypeLineEdit.emplace_back(lineEdit);
}
return m_containerTypeLineEdit[typeIndex];
}
}
#include <Editor/View/Dialogs/ContainerWizard/moc_ContainerWizard.cpp>
@@ -0,0 +1,128 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/PlatformDef.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QDialog>
#include <QIcon>
AZ_POP_DISABLE_WARNING
#include <AzCore/Component/EntityId.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <ScriptCanvas/Core/Core.h>
#endif
namespace Ui
{
class ContainerWizard;
}
namespace ScriptCanvasEditor
{
class ContainerTypeLineEdit;
class ContainerWizard
: public QDialog
{
Q_OBJECT
private:
typedef AZStd::unordered_set< AZ::TypeId > DataTypeSet;
public:
AZ_CLASS_ALLOCATOR(ContainerWizard, AZ::SystemAllocator, 0);
ContainerWizard(QWidget* parent = nullptr);
~ContainerWizard() override;
void SetActiveScriptCanvasId(const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
void RegisterType(const AZ::TypeId& dataType);
void ShowWizard(const AZ::TypeId& genericContainerType);
void accept() override;
void reject() override;
void hideEvent(QHideEvent* hideEvent) override;
bool eventFilter(QObject* object, QEvent* event) override;
const AZStd::unordered_map< AZ::Crc32, AZ::TypeId >& GetFinalTypeMapping() const;
public Q_SLOTS:
void ReparseDisplay();
void OnFinished(int result);
void OnContainerTypeChanged(int index);
void OnTypeChanged(int index, const AZ::TypeId& typeId);
void OnDataTypeMenuVisibilityChanged(bool visible);
void ValidateName(const QString& newName);
Q_SIGNALS:
void ContainerPinned(const AZ::TypeId& typeId);
void CreateContainerVariable(const AZStd::string& variableName, const AZ::TypeId& typeId);
protected:
void OnCreate();
void OnCancel();
void ClearDisplay();
void InitializeDisplay(const AZ::TypeId& typeId);
void PopulateMapDisplay();
void PopulateGeneralDisplay(const AZStd::string& patternFallback = "Type %i", const AZStd::string& singleTypeString = "Type", const AZStd::vector< AZStd::string >& typeLabels = AZStd::vector< AZStd::string>());
private:
void RegisterDataType(const AZ::TypeId& dataType);
void RegisterContainerType(const AZ::TypeId& containerType);
ContainerTypeLineEdit* GetLineEdit(int paramIndex);
AZ::SerializeContext* m_serializeContext;
ScriptCanvas::ScriptCanvasId m_activeScriptCanvasId;
QAction* m_validationAction;
QIcon m_invalidIcon;
bool m_dataTypeMenuVisibile;
bool m_releaseVariable;
AZ::u32 m_variableCounter;
AZ::TypeId m_genericType;
AZStd::vector< AZ::TypeId > m_containerTypes;
AZStd::vector< ContainerTypeLineEdit* > m_containerTypeLineEdit;
DataTypeSet m_genericContainerTypes;
AZStd::vector< AZStd::pair<AZStd::string, AZ::TypeId> > m_genericContainerTypeNames;
// Temporarily unused. When we can reflect combinations on demand, we can use this list to populate, rather then the pre-generated lists
AZStd::unordered_map< AZ::TypeId, AZStd::string > m_dataTypes;
AZStd::unordered_map< AZ::Crc32, DataTypeSet > m_containerDataTypeSets;
AZStd::unordered_map< AZ::Crc32, AZ::TypeId > m_finalContainerTypeIds;
AZStd::unique_ptr< Ui::ContainerWizard > m_ui;
};
}
@@ -0,0 +1,358 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ContainerWizard</class>
<widget class="QDialog" name="ContainerWizard">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>404</width>
<height>214</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Create Container</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>115</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Variable Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="variableName"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="containerLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>115</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Specifies the type of Container to create.</string>
</property>
<property name="text">
<string>Container Type</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="containerTypeBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="minimumSize">
<size>
<width>330</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="typeSelectionFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_4">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>115</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Pin To Variable List</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="createButton">
<property name="text">
<string>Create</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,56 @@
/*
* 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 "NewGraphDialog.h"
#include <QLineEdit>
#include <QPushButton>
#include "Editor/View/Dialogs/ui_NewGraphDialog.h"
namespace ScriptCanvasEditor
{
NewGraphDialog::NewGraphDialog(const QString& title, const QString& text, QWidget* pParent /*=nullptr*/)
: QDialog(pParent)
, ui(new Ui::NewGraphDialog)
, m_text(text)
{
ui->setupUi(this);
setWindowTitle(title);
QObject::connect(ui->GraphName, &QLineEdit::returnPressed, this, &NewGraphDialog::OnOK);
QObject::connect(ui->GraphName, &QLineEdit::textChanged, this, &NewGraphDialog::OnTextChanged);
QObject::connect(ui->ok, &QPushButton::clicked, this, &NewGraphDialog::OnOK);
QObject::connect(ui->cancel, &QPushButton::clicked, this, &QDialog::reject);
ui->ok->setEnabled(false);
}
void NewGraphDialog::OnTextChanged(const QString& text)
{
ui->ok->setEnabled(!text.isEmpty());
}
void NewGraphDialog::OnOK()
{
QString itemName = ui->GraphName->text();
m_text = itemName.toLocal8Bit().constData();
accept();
}
#include <Editor/View/Dialogs/moc_NewGraphDialog.cpp>
}
@@ -0,0 +1,46 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#endif
namespace Ui
{
class NewGraphDialog;
}
namespace ScriptCanvasEditor
{
class NewGraphDialog
: public QDialog
{
Q_OBJECT
public:
NewGraphDialog(const QString& title, const QString& text, QWidget* pParent = nullptr);
const QString& GetText() const { return m_text; }
protected:
void OnOK();
void OnTextChanged(const QString& text);
QString m_text;
Ui::NewGraphDialog* ui;
};
}
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewGraphDialog</class>
<widget class="QDialog" name="NewGraphDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>72</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="name">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="GraphName">
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ok">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,224 @@
/*
* 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 "Settings.h"
// qtextformat.h(365): warning C4251: 'QTextFormat::d': class 'QSharedDataPointer<QTextFormatPrivate>' needs to have dll-interface to be used by clients of class 'QTextFormat'
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QLineEdit>
#include <QPushButton>
#include <QKeyEvent>
AZ_POP_DISABLE_WARNING
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
#include <GraphCanvas/Editor/AssetEditorBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include "Editor/View/Dialogs/ui_Settings.h"
namespace ScriptCanvasEditor
{
SettingsDialog::SettingsDialog(const QString& title, ScriptCanvas::ScriptCanvasId scriptCanvasId, QWidget* pParent /*=nullptr*/)
: QDialog(pParent)
, ui(new Ui::SettingsDialog)
, m_scriptCanvasId(scriptCanvasId)
{
ui->setupUi(this);
setWindowTitle(title);
QObject::connect(ui->ok, &QPushButton::clicked, this, &SettingsDialog::OnOK);
QObject::connect(ui->cancel, &QPushButton::clicked, this, &SettingsDialog::OnCancel);
if (m_scriptCanvasId.IsValid())
{
SetType(SettingsType::Graph);
}
else
{
SetType(SettingsType::General);
}
m_revertOnClose = true;
}
void SettingsDialog::ConfigurePropertyEditor(AzToolsFramework::ReflectedPropertyEditor* editor)
{
editor->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
editor->SetHideRootProperties(false);
editor->SetDynamicEditDataProvider(nullptr);
editor->ExpandAll();
editor->InvalidateAll();
}
SettingsDialog::~SettingsDialog()
{
if (m_revertOnClose)
{
RevertSettings();
}
ui->propertyEditor->ClearInstances();
}
void SettingsDialog::OnTextChanged(const QString& text)
{
ui->ok->setEnabled(!text.isEmpty());
}
void SettingsDialog::OnOK()
{
m_revertOnClose = false;
AZ::UserSettingsOwnerRequestBus::Event(AZ::UserSettings::CT_LOCAL, &AZ::UserSettingsOwnerRequests::SaveSettings);
GraphCanvas::AssetEditorSettingsNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorSettingsNotifications::OnSettingsChanged);
accept();
}
void Settings::Reflect(AZ::ReflectContext* reflection)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<Settings>()
->Version(0)
->Field("EnableLogging", &Settings::m_enableLogging)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<Settings>("Script Canvas Settings", "Per-graph Script Canvas settings")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20))
->DataElement(AZ::Edit::UIHandlers::Default, &Settings::m_enableLogging, "Logging", "Will enable logging for this Script Canvas graph")
;
}
}
}
void SettingsDialog::OnCancel()
{
RevertSettings();
close();
}
void SettingsDialog::SetType(SettingsType settingsType)
{
AZ::SerializeContext* context = nullptr;
{
AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationRequests::GetSerializeContext);
AZ_Assert(context, "We should have a valid context!");
}
AZ_Warning("SetingsDialog", settingsType != SettingsType::None,
"Cannot set up settings for None type. Please choose a valid type.");
// SettingsType::None
ui->generalLabel->setVisible(false);
ui->previewSettingsPropertyEditor->setVisible(false);
ui->previewSettingsPropertyEditor->SetAutoResizeLabels(true);
ui->graphLabel->setVisible(false);
ui->propertyEditor->setVisible(false);
ui->propertyEditor->SetAutoResizeLabels(true);
if (settingsType == SettingsType::Graph || settingsType == SettingsType::All)
{
ui->graphLabel->setVisible(true);
ui->propertyEditor->setVisible(true);
SetupGraphSettings(context);
}
if (settingsType == SettingsType::General || settingsType == SettingsType::All)
{
ui->generalLabel->setVisible(true);
ui->previewSettingsPropertyEditor->setVisible(true);
SetupGeneralSettings(context);
}
m_settingsType = settingsType;
}
void SettingsDialog::SetupGeneralSettings(AZ::SerializeContext* context)
{
// General properties
AZStd::intrusive_ptr<EditorSettings::ScriptCanvasEditorSettings> previewSettings =
AZ::UserSettings::CreateFind<EditorSettings::ScriptCanvasEditorSettings>(
AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL);
// Store a copy to revert if needed.
m_originalEditorSettings = *previewSettings;
ui->previewSettingsPropertyEditor->Setup(context, nullptr, false, 210);
ui->previewSettingsPropertyEditor->AddInstance(previewSettings.get(), previewSettings->RTTI_GetType());
ui->previewSettingsPropertyEditor->setObjectName("ui->previewSettingsPropertyEditor");
ConfigurePropertyEditor(ui->previewSettingsPropertyEditor);
}
void SettingsDialog::SetupGraphSettings(AZ::SerializeContext* context)
{
if (m_scriptCanvasId.IsValid())
{
AZStd::intrusive_ptr<Settings> settings =
AZ::UserSettings::CreateFind<Settings>(AZ::Crc32(m_scriptCanvasId.ToString().c_str()),
AZ::UserSettings::CT_LOCAL);
// Store a copy to revert if needed.
m_originalSettings = *settings;
ui->propertyEditor->setDisabled(false);
ui->propertyEditor->Setup(context, nullptr, false, 210);
ui->propertyEditor->AddInstance(settings.get(), settings->RTTI_GetType());
ui->propertyEditor->setObjectName("ui->propertyEditor");
ui->propertyEditor->SetSavedStateKey(AZ::Crc32(m_scriptCanvasId.ToString().c_str()));
ConfigurePropertyEditor(ui->propertyEditor);
}
else
{
ui->propertyEditor->setDisabled(true);
}
}
void SettingsDialog::RevertSettings()
{
if (m_settingsType == SettingsType::Graph || m_settingsType == SettingsType::All)
{
if (m_scriptCanvasId.IsValid())
{
AZStd::intrusive_ptr<Settings> settings =
AZ::UserSettings::CreateFind<Settings>(AZ::Crc32(m_scriptCanvasId.ToString().c_str()),
AZ::UserSettings::CT_LOCAL);
// Revert the stored copy, no changes will be stored.
*settings = m_originalSettings;
}
}
if (m_settingsType == SettingsType::General || m_settingsType == SettingsType::All)
{
// General properties
AZStd::intrusive_ptr<EditorSettings::ScriptCanvasEditorSettings> previewSettings =
AZ::UserSettings::CreateFind<EditorSettings::ScriptCanvasEditorSettings>(
AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL);
*previewSettings = m_originalEditorSettings;
}
m_revertOnClose = false;
}
#include <Editor/View/Dialogs/moc_Settings.cpp>
}
@@ -0,0 +1,99 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QDialog>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzCore/Component/EntityId.h>
#include <Editor/Settings.h>
#include <ScriptCanvas/Core/Core.h>
#endif
namespace Ui
{
class SettingsDialog;
}
namespace AzToolsFramework
{
class ReflectedPropertyEditor;
}
namespace ScriptCanvasEditor
{
class Settings
: public AZ::UserSettings
{
public:
AZ_RTTI(Settings, "{E3B5DE71-FB4E-472C-BD2A-BD180E68B9A6}", AZ::UserSettings);
AZ_CLASS_ALLOCATOR(Settings, AZ::SystemAllocator, 0);
Settings()
: m_enableLogging(false)
{}
bool m_enableLogging;
static void Reflect(AZ::ReflectContext* reflection);
};
enum class SettingsType
{
None,
All,
General,
Graph
};
class SettingsDialog
: public QDialog
{
Q_OBJECT
public:
SettingsDialog(const QString& title, ScriptCanvas::ScriptCanvasId scriptCanvasId, QWidget* pParent = nullptr);
~SettingsDialog() override;
const QString& GetText() const { return m_text; }
protected:
void OnOK();
void OnCancel();
void OnTextChanged(const QString& text);
void ConfigurePropertyEditor(AzToolsFramework::ReflectedPropertyEditor*);
private:
void SetType(SettingsType settingsType);
void SetupGeneralSettings(AZ::SerializeContext* context);
void SetupGraphSettings(AZ::SerializeContext* context);
void RevertSettings();
QString m_text;
ScriptCanvas::ScriptCanvasId m_scriptCanvasId;
bool m_revertOnClose;
Settings m_originalSettings;
EditorSettings::ScriptCanvasEditorSettings m_originalEditorSettings;
SettingsType m_settingsType = SettingsType::None;
Ui::SettingsDialog* ui;
};
}
@@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SettingsDialog</class>
<widget class="QDialog" name="SettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>400</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>400</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
<height>400</height>
</size>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<property name="bottomMargin">
<number>12</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>476</width>
<height>76</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="scrollAreaLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="graphLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Graph Preferences</string>
</property>
</widget>
</item>
<item>
<widget class="AzToolsFramework::ReflectedPropertyEditor" name="propertyEditor" native="true">
</widget>
</item>
<item>
<widget class="QLabel" name="generalLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>General Preferences</string>
</property>
</widget>
</item>
<item>
<widget class="AzToolsFramework::ReflectedPropertyEditor" name="previewSettingsPropertyEditor" native="true">
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ok">
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzToolsFramework::ReflectedPropertyEditor</class>
<extends>QWidget</extends>
<header location="global">AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,54 @@
/*
* 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 "UnsavedChangesDialog.h"
#include <QPushButton>
#include "Editor/View/Dialogs/ui_UnsavedChangesDialog.h"
namespace ScriptCanvasEditor
{
UnsavedChangesDialog::UnsavedChangesDialog(const QString& filename, QWidget* pParent /*=nullptr*/)
: QDialog(pParent)
, ui(new Ui::UnsavedChangesDialog)
{
ui->setupUi(this);
ui->m_saveLabel->setText(filename);
QObject::connect(ui->m_saveButton, &QPushButton::clicked, this, &UnsavedChangesDialog::OnSaveButton);
QObject::connect(ui->m_continueButton, &QPushButton::clicked, this, &UnsavedChangesDialog::OnContinueWithoutSavingButton);
QObject::connect(ui->m_cancelButton, &QPushButton::clicked, this, &UnsavedChangesDialog::OnCancelWithoutSavingButton);
}
void UnsavedChangesDialog::OnSaveButton(bool)
{
m_result = UnsavedChangesOptions::SAVE;
accept();
}
void UnsavedChangesDialog::OnContinueWithoutSavingButton(bool)
{
m_result = UnsavedChangesOptions::CONTINUE_WITHOUT_SAVING;
accept();
}
void UnsavedChangesDialog::OnCancelWithoutSavingButton([[maybe_unused]] bool clicked)
{
m_result = UnsavedChangesOptions::CANCEL_WITHOUT_SAVING;
accept();
}
#include <Editor/View/Dialogs/moc_UnsavedChangesDialog.cpp>
}
@@ -0,0 +1,54 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <QDialog>
#endif
namespace Ui
{
class UnsavedChangesDialog;
}
namespace ScriptCanvasEditor
{
enum class UnsavedChangesOptions
{
SAVE,
CONTINUE_WITHOUT_SAVING,
CANCEL_WITHOUT_SAVING,
INVALID
};
class UnsavedChangesDialog
: public QDialog
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(UnsavedChangesDialog, AZ::SystemAllocator, 0);
UnsavedChangesDialog(const QString& filename, QWidget* pParent = nullptr);
UnsavedChangesOptions GetResult() const { return m_result; }
protected:
void OnSaveButton(bool clicked);
void OnContinueWithoutSavingButton(bool clicked);
void OnCancelWithoutSavingButton(bool clicked);
Ui::UnsavedChangesDialog* ui;
UnsavedChangesOptions m_result = UnsavedChangesOptions::INVALID;
};
}
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UnsavedChangesDialog</class>
<widget class="QDialog" name="SaveChangesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>404</width>
<height>75</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="m_saveLabel">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>391</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>Would you like to save changes?</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="m_saveButton">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_continueButton">
<property name="text">
<string>Don't Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<property name="windowTitle">
<string>Save Script Canvas Changes?</string>
</property>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,29 @@
/*
* 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 <AzCore/Math/Uuid.h>
#include <AzCore/EBus/EBus.h>
namespace ScriptCanvasEditor
{
class AssetGraphScene : public AZ::EBusTraits
{
public:
virtual AZ::EntityId FindEditorNodeIdByAssetNodeId(const AZ::Data::AssetId& assetId, AZ::EntityId assetNodeId) const = 0;
virtual AZ::EntityId FindAssetNodeIdByEditorNodeId(const AZ::Data::AssetId& assetId, AZ::EntityId editorNodeId) const = 0;
};
using AssetGraphSceneBus = AZ::EBus<AssetGraphScene>;
}
@@ -0,0 +1,227 @@
/*
* 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 "CanvasWidget.h"
// qpainter.h(465): warning C4251: 'QPainter::d_ptr': class 'QScopedPointer<QPainterPrivate,QScopedPointerDeleter<T>>' needs to have dll-interface to be used by clients of class 'QPainter'
// qpainter.h(450): warning C4800: 'QFlags<QPainter::RenderHint>::Int': forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QVBoxLayout>
#include <QLabel>
#include <QGraphicsView>
#include <QPushButton>
AZ_POP_DISABLE_WARNING
#include <AzCore/Casting/numeric_cast.h>
#include "Editor/View/Widgets/ui_CanvasWidget.h"
#include <GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h>
#include <GraphCanvas/Widgets/MiniMapGraphicsView/MiniMapGraphicsView.h>
#include <GraphCanvas/GraphCanvasBus.h>
#include <Debugger/Bus.h>
#include <Core/Graph.h>
#include <Editor/View/Dialogs/Settings.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h>
namespace ScriptCanvasEditor
{
namespace Widget
{
CanvasWidget::CanvasWidget(const AZ::Data::AssetId& assetId, QWidget* parent)
: QWidget(parent)
, ui(new Ui::CanvasWidget())
, m_attached(false)
, m_assetId(assetId)
, m_graphicsView(nullptr)
, m_miniMapView(nullptr)
{
ui->setupUi(this);
ui->m_debuggingControls->hide();
SetupGraphicsView();
connect(ui->m_debugAttach, &QPushButton::clicked, this, &CanvasWidget::OnClicked);
}
CanvasWidget::~CanvasWidget()
{
hide();
}
void CanvasWidget::SetDefaultBorderColor(AZ::Color defaultBorderColor)
{
m_defaultBorderColor = defaultBorderColor;
QString styleSheet = QString("QFrame#graphicsViewFrame { background-color: rgb(%1,%2,%3) }").arg(defaultBorderColor.GetR8()).arg(defaultBorderColor.GetG8()).arg(defaultBorderColor.GetB8());
ui->graphicsViewFrame->setStyleSheet(styleSheet);
}
void CanvasWidget::ShowScene(const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
{
EditorGraphRequests* editorGraphRequests = EditorGraphRequestBus::FindFirstHandler(scriptCanvasId);
editorGraphRequests->SetAssetId(m_assetId);
editorGraphRequests->CreateGraphCanvasScene();
AZ::EntityId graphCanvasSceneId = editorGraphRequests->GetGraphCanvasGraphId();
m_graphicsView->SetScene(graphCanvasSceneId);
m_scriptCanvasId = scriptCanvasId;
}
const GraphCanvas::ViewId& CanvasWidget::GetViewId() const
{
return m_graphicsView->GetViewId();
}
void CanvasWidget::EnableView()
{
if (!isEnabled())
{
setDisabled(false);
if (m_disabledOverlay.IsValid() && m_graphicsView)
{
GraphCanvas::SceneRequestBus::Event(m_graphicsView->GetScene(), &GraphCanvas::SceneRequests::CancelGraphicsEffect, m_disabledOverlay);
m_disabledOverlay = GraphCanvas::GraphicsEffectId();
}
}
}
void CanvasWidget::DisableView()
{
if (isEnabled())
{
setDisabled(true);
if (m_graphicsView)
{
AZ::EntityId graphCanvasSceneId = m_graphicsView->GetScene();
GraphCanvas::OccluderConfiguration occluderConfiguration;
occluderConfiguration.m_bounds = m_graphicsView->sceneRect();
occluderConfiguration.m_opacity = 0.5f;
occluderConfiguration.m_renderColor = QColor(0, 0, 0);
occluderConfiguration.m_zValue = 100000;
GraphCanvas::SceneRequestBus::EventResult(m_disabledOverlay, graphCanvasSceneId, &GraphCanvas::SceneRequests::CreateOccluder, occluderConfiguration);
}
}
}
void CanvasWidget::SetupGraphicsView()
{
const bool registerMenuActions = false;
m_graphicsView = aznew GraphCanvas::GraphCanvasGraphicsView(nullptr, registerMenuActions);
AZ_Assert(m_graphicsView, "Could Canvas Widget unable to create CanvasGraphicsView object.");
if (m_graphicsView)
{
ui->graphicsViewFrame->layout()->addWidget(m_graphicsView);
m_graphicsView->show();
m_graphicsView->SetEditorId(ScriptCanvasEditor::AssetEditorId);
// Temporary shortcut for docking the MiniMap. Removed until we fix up the MiniMap
/*
{
QAction* action = new QAction(m_graphicsView);
action->setShortcut(QKeySequence(Qt::Key_M));
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(action, &QAction::triggered,
[this]()
{
if (!m_graphicsView->rubberBandRect().isNull() || QApplication::mouseButtons() || m_graphicsView->GetIsEditing())
{
// Nothing to do.
return;
}
if (m_miniMapView)
{
// Cycle the position.
m_miniMapPosition = static_cast<MiniMapPosition>((m_miniMapPosition + 1) % MM_Position_Count);
}
else
{
m_miniMapView = aznew GraphCanvas::MiniMapGraphicsView(0 , false, m_graphicsView->GetScene(), m_graphicsView);
}
// Apply position.
PositionMiniMap();
});
m_graphicsView->addAction(action);
}*/
}
}
void CanvasWidget::showEvent(QShowEvent* /*event*/)
{
ui->m_debugAttach->setText(m_attached ? "Debugging: On" : "Debugging: Off");
EditorGraphRequestBus::Event(m_scriptCanvasId, &EditorGraphRequests::OnGraphCanvasSceneVisible);
}
void CanvasWidget::PositionMiniMap()
{
if (!(m_miniMapView && m_graphicsView))
{
// Nothing to do.
return;
}
const QRect& parentRect = m_graphicsView->frameGeometry();
if (m_miniMapPosition == MM_Upper_Left)
{
m_miniMapView->move(0, 0);
}
else if (m_miniMapPosition == MM_Upper_Right)
{
m_miniMapView->move(parentRect.width() - m_miniMapView->size().width(), 0);
}
else if (m_miniMapPosition == MM_Lower_Right)
{
m_miniMapView->move(parentRect.width() - m_miniMapView->size().width(), parentRect.height() - m_miniMapView->size().height());
}
else if (m_miniMapPosition == MM_Lower_Left)
{
m_miniMapView->move(0, parentRect.height() - m_miniMapView->size().height());
}
m_miniMapView->setVisible(m_miniMapPosition != MM_Not_Visible);
}
void CanvasWidget::resizeEvent(QResizeEvent *ev)
{
QWidget::resizeEvent(ev);
PositionMiniMap();
}
void CanvasWidget::OnClicked()
{
}
#include <Editor/View/Widgets/moc_CanvasWidget.cpp>
}
}
@@ -0,0 +1,100 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Component/EntityId.h>
#include <Debugger/Bus.h>
#include <AzCore/Asset/AssetCommon.h>
#include <GraphCanvas/Components/ViewBus.h>
#endif
class QVBoxLayout;
namespace Ui
{
class CanvasWidget;
}
namespace GraphCanvas
{
class GraphCanvasGraphicsView;
class MiniMapGraphicsView;
}
namespace ScriptCanvasEditor
{
namespace Widget
{
class CanvasWidget
: public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(CanvasWidget, AZ::SystemAllocator, 0);
CanvasWidget(const AZ::Data::AssetId& assetId, QWidget* parent = nullptr);
~CanvasWidget() override;
void SetDefaultBorderColor(AZ::Color defaultBorderColor);
void ShowScene(const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
const GraphCanvas::ViewId& GetViewId() const;
void EnableView();
void DisableView();
protected:
void resizeEvent(QResizeEvent *ev);
void OnClicked();
bool m_attached;
void SetupGraphicsView();
AZ::Data::AssetId m_assetId;
AZStd::unique_ptr<Ui::CanvasWidget> ui;
void showEvent(QShowEvent *event) override;
private:
enum MiniMapPosition
{
MM_Not_Visible,
MM_Upper_Left,
MM_Upper_Right,
MM_Lower_Right,
MM_Lower_Left,
MM_Position_Count
};
void PositionMiniMap();
AZ::Color m_defaultBorderColor;
ScriptCanvas::ScriptCanvasId m_scriptCanvasId;
GraphCanvas::GraphCanvasGraphicsView* m_graphicsView;
GraphCanvas::MiniMapGraphicsView* m_miniMapView;
MiniMapPosition m_miniMapPosition = MM_Upper_Left;
GraphCanvas::GraphicsEffectId m_disabledOverlay;
};
}
}
@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CanvasWidget</class>
<widget class="QWidget" name="CanvasWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>667</width>
<height>505</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="graphicsViewFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>2</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="m_debuggingControls">
<property name="enabled">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QLabel" name="m_label0">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Debug</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_debugAttach">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Debugging: OFF</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,525 @@
/*
* 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 "CommandLine.h"
#include <QString>
#include <QKeyEvent>
#include <QCompleter>
#include <QGraphicsItem>
#include <QGraphicsView>
#include "Editor/View/Widgets/ui_CommandLine.h"
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <Core/Attributes.h>
#include <Core/Node.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <Editor/Nodes/NodeUtils.h>
#include <Editor/QtMetaTypes.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/ViewBus.h>
#include <GraphCanvas/Components/GeometryBus.h>
namespace
{
using namespace ScriptCanvasEditor;
using namespace ScriptCanvasEditor::Widget;
void CreateSelectedNodes(CommandLine* commandLine,
AZStd::unique_ptr<Ui::CommandLine>& ui,
AZ::SerializeContext* serializeContext)
{
if (!ui->commandList->selectionModel())
{
return;
}
if (ui->commandList->selectionModel()->selectedIndexes().empty())
{
// Nothing selected.
return;
}
CommandListDataProxyModel* dataModel = qobject_cast<CommandListDataProxyModel*>(ui->commandList->model());
if (!dataModel)
{
return;
}
ScriptCanvas::ScriptCanvasId scriptCanvasId;
ScriptCanvasEditor::GeneralRequestBus::BroadcastResult(scriptCanvasId, &ScriptCanvasEditor::GeneralRequests::GetActiveScriptCanvasId);
AZ::EntityId graphCanvasGraphId;
ScriptCanvasEditor::GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &ScriptCanvasEditor::GeneralRequests::GetActiveGraphCanvasGraphId);
if (!(scriptCanvasId.IsValid() &&
graphCanvasGraphId.IsValid()))
{
// Nothing active.
return;
}
// Create the nodes in a horizontal list at the top of the canvas.
AZ::Vector2 pos(20.0f, -100.0f);
for (const auto& index : ui->commandList->selectionModel()->selectedIndexes())
{
if (index.column() != CommandListDataModel::ColumnIndex::Command)
{
continue;
}
AZ::Uuid type = dataModel->data(index, CommandListDataModel::CustomRole::Types).value<AZ::Uuid>();
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(type);
AZ_Assert(classData, "Failed to find ClassData for ID: %s", type.ToString<AZStd::string>().data());
ScriptCanvasEditor::Nodes::StyleConfiguration styleConfiguration;
NodeIdPair nodePair = ScriptCanvasEditor::Nodes::CreateNode(type, scriptCanvasId, styleConfiguration);
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, nodePair.m_graphCanvasId, pos);
// The next position to create a node at.
// IMPORTANT: This SHOULD be using GraphCanvas::GeometryRequests::GetWidth, but
// GetWidth is currently returning zero, so we'll TEMPORARILY use a fixed value.
pos += AZ::Vector2(125.0f, 0.0f);
}
commandLine->hide();
}
} // anonymous namespace.
namespace ScriptCanvasEditor
{
namespace Widget
{
// CommandListDataModel
/////////////////////////////////////////////////////////////////////////////////////////////
CommandListDataModel::CommandListDataModel([[maybe_unused]] QWidget* parent /*= nullptr*/)
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
serializeContext->EnumerateDerived<ScriptCanvas::Node>(
[this](const AZ::SerializeContext::ClassData* classData, [[maybe_unused]] const AZ::Uuid& classUuid) -> bool
{
if (classData && classData->m_editData)
{
bool add = true;
if (auto editorElementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData))
{
if (auto excludeAttribute = editorElementData->FindAttribute(AZ::Script::Attributes::ExcludeFrom))
{
auto excludeAttributeData = azdynamic_cast<const AZ::Edit::AttributeData<bool>*>(excludeAttribute);
if (excludeAttributeData)
{
add = false;
}
}
}
if (add)
{
m_nodeTypes.push_back(classData->m_typeId);
}
}
return true;
}
);
}
QModelIndex CommandListDataModel::index(int row, int column, const QModelIndex& parent /*= QModelIndex()*/) const
{
if (row >= rowCount(parent) || column >= columnCount(parent))
{
return QModelIndex();
}
return createIndex(row, column);
}
QModelIndex CommandListDataModel::parent([[maybe_unused]] const QModelIndex& child) const
{
return QModelIndex();
}
int CommandListDataModel::rowCount([[maybe_unused]] const QModelIndex& parent /*= QModelIndex()*/) const
{
return static_cast<int>(m_nodeTypes.size());
}
int CommandListDataModel::columnCount([[maybe_unused]] const QModelIndex& parent /*= QModelIndex()*/) const
{
return ColumnIndex::Count;
}
QVariant CommandListDataModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
if (role == Qt::DisplayRole)
{
if (index.row() == 0)
{
if (index.column() == 0)
{
return QVariant(QString(tr("No results found.")));
}
else
if (index.column() > 0)
{
return QVariant();
}
}
AZ::Uuid nodeType = m_nodeTypes[index.row()];
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType);
if (index.column() == ColumnIndex::Command)
{
return QVariant(QString(classData->m_name));
}
if (index.column() == ColumnIndex::Description)
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
}
if (index.column() == ColumnIndex::Trail)
{
return QVariant(QString(""));
}
}
switch (role)
{
case CustomRole::Types:
{
AZ::Uuid nodeType = m_nodeTypes[index.row()];
return QVariant::fromValue<AZ::Uuid>(nodeType);
}
break;
case CustomRole::Node:
{
AZ::Uuid nodeType = m_nodeTypes[index.row()];
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(nodeType);
if (index.column() == ColumnIndex::Command)
{
return QVariant(QString(classData->m_name));
}
if (index.column() == ColumnIndex::Description)
{
return QVariant(QString(classData->m_editData ? classData->m_editData->m_description : tr("No description provided.")));
}
if (index.column() == ColumnIndex::Trail)
{
return QVariant(QString(""));
}
}
break;
default:
break;
}
return QVariant();
}
Qt::ItemFlags CommandListDataModel::flags(const QModelIndex& index) const
{
return QAbstractTableModel::flags(index);
}
bool CommandListDataModel::HasMatches(const AZStd::string& input)
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
for (const auto& entry : m_nodeTypes)
{
const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(entry);
if (classData)
{
QString name = QString(classData->m_name);
if (name.startsWith(input.c_str(), Qt::CaseSensitivity::CaseInsensitive))
{
return true;
}
}
}
return false;
}
// CommandLineEdit
/////////////////////////////////////////////////////////////////////////////////////////////
namespace
{
QString s_defaultText = QStringLiteral("Press ? for help");
}
CommandLineEdit::CommandLineEdit(QWidget* parent /*= nullptr*/)
: QLineEdit(parent)
, m_empty(true)
, m_defaultText(s_defaultText)
{
ResetState();
connect(this, &QLineEdit::textChanged, this, &CommandLineEdit::onTextChanged);
connect(this, &QLineEdit::textEdited, this, &CommandLineEdit::onTextEdited);
connect(this, &QLineEdit::returnPressed, this, &CommandLineEdit::onReturnPressed);
}
void CommandLineEdit::onReturnPressed()
{
}
void CommandLineEdit::onTextChanged([[maybe_unused]] const QString& text)
{
}
void CommandLineEdit::onTextEdited(const QString& text)
{
// Execute the command
(void)text;
if (text.isEmpty())
{
ResetState();
}
}
void CommandLineEdit::focusInEvent(QFocusEvent* e)
{
QLineEdit::focusInEvent(e);
Q_EMIT(onFocusChange(true));
}
void CommandLineEdit::focusOutEvent(QFocusEvent* e)
{
QLineEdit::focusInEvent(e);
Q_EMIT(onFocusChange(true));
}
void CommandLineEdit::ResetState()
{
m_empty = true;
setText(m_defaultText);
}
void CommandLineEdit::keyReleaseEvent(QKeyEvent* event)
{
Q_EMIT(onKeyReleased(event));
}
void CommandLineEdit::keyPressEvent(QKeyEvent* event)
{
switch (event->key())
{
case Qt::Key_Enter:
case Qt::Key_Return:
{
// Invoke the command
// TODO: trigger invoke
// CommandRequestBus::Broadcast(&CommandRequest::Invoke, text().toStdString().c_str());
ResetState();
qobject_cast<QWidget*>(parent())->hide();
}
break;
case Qt::Key_Backspace:
if (m_empty)
{
break;
}
QLineEdit::keyPressEvent(event);
break;
case Qt::Key_Escape:
ResetState();
qobject_cast<QWidget*>(parent())->hide();
return;
default:
if (m_empty)
{
setText("");
m_empty = false;
}
QLineEdit::keyPressEvent(event);
break;
}
}
// CommandLineList
/////////////////////////////////////////////////////////////////////////////////////////////
CommandLineList::CommandLineList(QWidget* parent /*= nullptr*/)
: QTableView(parent)
{
}
// CommandListDataProxyModel
/////////////////////////////////////////////////////////////////////////////////////////////
CommandListDataProxyModel::CommandListDataProxyModel(QObject* parent /*= nullptr*/)
: QSortFilterProxyModel(parent)
{
QStringList commands;
CommandListDataModel* commandListData = new CommandListDataModel();
for (int i = 0; i < commandListData->rowCount(); ++i)
{
QModelIndex index = commandListData->index(i, CommandListDataModel::ColumnIndex::Command);
QString command = commandListData->data(index, CommandListDataModel::CustomRole::Node).toString();
commands.push_back(command);
}
m_completer = new QCompleter(commands);
m_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
m_completer->setCaseSensitivity(Qt::CaseInsensitive);
}
bool CommandListDataProxyModel::filterAcceptsRow(int sourceRow, [[maybe_unused]] const QModelIndex& sourceParent) const
{
CommandListDataModel* dataModel = qobject_cast<CommandListDataModel*>(sourceModel());
if (sourceRow < 0 || sourceRow >= dataModel->rowCount())
{
return false;
}
if (m_input.empty() || m_input.compare(s_defaultText.toStdString().c_str()) == 0)
{
return false;
}
if (AzFramework::StringFunc::FirstCharacter(m_input.c_str()) == '?')
{
if (sourceRow > 0)
{
return true;
}
else
{
return false;
}
}
QModelIndex index = dataModel->index(sourceRow, CommandListDataModel::ColumnIndex::Command);
QString sourceStr = dataModel->data(index).toString();
if (sourceRow > 0 && sourceStr.startsWith(m_input.c_str(), Qt::CaseSensitivity::CaseInsensitive))
{
return true;
}
else
if (sourceRow == 0)
{
if (!dataModel->HasMatches(m_input))
{
return true;
}
}
return false;
}
// CommandLine
/////////////////////////////////////////////////////////////////////////////////////////////
CommandLine::CommandLine(QWidget* parent /*= nullptr*/)
: QWidget(parent)
, ui(new Ui::CommandLine())
{
ui->setupUi(this);
CommandListDataModel* commandListDataModel = new CommandListDataModel();
CommandListDataProxyModel* commandListDataProxyModel = new CommandListDataProxyModel();
commandListDataProxyModel->setSourceModel(commandListDataModel);
ui->commandList->setModel(commandListDataProxyModel);
connect(ui->commandText, &QLineEdit::textChanged, this, &CommandLine::onTextChanged);
connect(ui->commandText, &CommandLineEdit::onKeyReleased, this, &CommandLine::onEditKeyReleaseEvent);
connect(ui->commandList, &CommandLineList::onKeyReleased, this, &CommandLine::onListKeyReleaseEvent);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::Command, 250);
ui->commandList->setColumnWidth(CommandListDataModel::ColumnIndex::Description, 1000);
}
void CommandLine::onTextChanged(const QString& text)
{
CommandListDataProxyModel* model = qobject_cast<CommandListDataProxyModel*>(ui->commandList->model());
if (model)
{
model->SetInput(text.toStdString().c_str());
}
}
void CommandLine::onListKeyReleaseEvent(QKeyEvent* event)
{
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
switch (event->key())
{
case Qt::Key_Up:
if (ui->commandList->selectionModel())
{
if (ui->commandList->selectionModel()->selectedIndexes().empty() || ui->commandList->selectionModel()->selectedIndexes().at(0).row() == 0)
{
ui->commandText->setFocus();
}
}
break;
case Qt::Key_Escape:
hide();
break;
case Qt::Key_Enter:
case Qt::Key_Return:
CreateSelectedNodes(this, ui, serializeContext);
break;
}
}
void CommandLine::onEditKeyReleaseEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Down)
{
ui->commandList->setFocus();
ui->commandList->selectRow(0);
}
}
void CommandLine::showEvent(QShowEvent* event)
{
QWidget::showEvent(event);
ui->commandText->ResetState();
ui->commandText->setFocus(Qt::PopupFocusReason);
}
#include <Editor/View/Widgets/moc_CommandLine.cpp>
}
}
@@ -0,0 +1,171 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QObject>
#include <QDialog>
#include <QLineEdit>
#include <QTableView>
#include <QSortFilterProxyModel>
#include <QAbstractTableModel>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#endif
namespace Ui
{
class CommandLine;
}
namespace ScriptCanvasEditor
{
namespace Widget
{
// TODO #lsempe: this deserves its own file
// CommandListDataModel
/////////////////////////////////////////////////////////////////////////////////////////////
class CommandListDataModel : public QAbstractTableModel
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(CommandListDataModel, AZ::SystemAllocator, 0);
enum ColumnIndex
{
Command,
Description,
Trail,
Count
};
enum CustomRole
{
Node = Qt::UserRole,
Types,
EBusSender,
EBusHandler,
Commands
};
CommandListDataModel(QWidget* parent = nullptr);
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool HasMatches(const AZStd::string& input);
protected:
AZStd::vector<AZ::Uuid> m_nodeTypes;
};
class CommandListDataProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(CommandListDataProxyModel, AZ::SystemAllocator, 0);
CommandListDataProxyModel(QObject* parent = nullptr);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
void SetInput(const AZStd::string& input)
{
m_input = input;
invalidate();
}
protected:
QCompleter* m_completer;
AZStd::string m_input;
};
// CommandLineEdit
/////////////////////////////////////////////////////////////////////////////////////////////
class CommandLineEdit : public QLineEdit
{
Q_OBJECT
public:
CommandLineEdit(QWidget* parent = nullptr);
void ResetState();
Q_SIGNALS:
void onFocusChange(bool focused);
void onKeyReleased(QKeyEvent*);
protected:
void focusInEvent(QFocusEvent*) override;
void focusOutEvent(QFocusEvent*) override;
void keyPressEvent(QKeyEvent *) override;
void keyReleaseEvent(QKeyEvent*) override;
void onTextChanged(const QString&);
void onTextEdited(const QString&);
void onReturnPressed();
bool m_empty;
const QString m_defaultText;
};
// CommandLineList
/////////////////////////////////////////////////////////////////////////////////////////////
class CommandLineList : public QTableView
{
Q_OBJECT
public:
CommandLineList(QWidget* parent = nullptr);
Q_SIGNALS:
void onKeyReleased(QKeyEvent*);
protected:
void keyReleaseEvent(QKeyEvent* event) override
{
Q_EMIT(onKeyReleased(event));
}
};
// CommandLine
/////////////////////////////////////////////////////////////////////////////////////////////
class CommandLine
: public QWidget
{
Q_OBJECT
public:
CommandLine(QWidget* object = nullptr);
void showEvent(QShowEvent *event) override;
void onTextChanged(const QString&);
void onEditKeyReleaseEvent(QKeyEvent*);
void onListKeyReleaseEvent(QKeyEvent*);
AZStd::unique_ptr<Ui::CommandLine> ui;
};
}
}
@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CommandLine</class>
<widget class="QWidget" name="commandLine">
<property name="minimumSize">
<size>
<width>120</width>
<height>400</height>
</size>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="ScriptCanvasEditor::Widget::CommandLineEdit" name="commandText">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>261</width>
<height>20</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="ScriptCanvasEditor::Widget::CommandLineList" name="commandList">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>261</width>
<height>20</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">
QTableView { background-color: rgba(15,15,15,0.1); }
</string>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>220</height>
</size>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerStretchLastSection">
<bool>true</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ScriptCanvasEditor::Widget::CommandLineEdit</class>
<extends>QLineEdit</extends>
<header>Editor/View/Widgets/CommandLine.h</header>
</customwidget>
<customwidget>
<class>ScriptCanvasEditor::Widget::CommandLineList</class>
<extends>QTableView</extends>
<header>Editor/View/Widgets/CommandLine.h</header>
</customwidget>
</customwidgets>
</ui>
@@ -0,0 +1,392 @@
/*
* 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 <qaction.h>
#include <qevent.h>
#include <qheaderview.h>
#include <qitemselectionmodel.h>
#include <qscrollbar.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <GraphCanvas/Types/TranslationTypes.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <Editor/View/Widgets/DataTypePalette/DataTypePaletteModel.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/Settings.h>
#include <Editor/Translation/TranslationHelper.h>
#include <Editor/QtMetaTypes.h>
#include <ScriptCanvas/Data/DataRegistry.h>
namespace ScriptCanvasEditor
{
/////////////////////////
// DataTypePaletteModel
/////////////////////////
DataTypePaletteModel::DataTypePaletteModel(QObject* parent)
: QAbstractTableModel(parent)
, m_pinIcon(":/ScriptCanvasEditorResources/Resources/pin.png")
{
}
int DataTypePaletteModel::columnCount(const QModelIndex&) const
{
return ColumnIndex::Count;
}
int DataTypePaletteModel::rowCount([[maybe_unused]] const QModelIndex& parent) const
{
return aznumeric_cast<int>(m_variableTypes.size());
}
QVariant DataTypePaletteModel::data(const QModelIndex &index, int role /*= Qt::DisplayRole*/) const
{
AZ::Uuid typeId = FindTypeIdForIndex(index);
switch (role)
{
case Qt::DisplayRole:
{
if (index.column() == ColumnIndex::Type)
{
AZStd::string typeString = FindTypeNameForTypeId(typeId);
return QString(typeString.c_str());
}
}
break;
case Qt::DecorationRole:
{
if (index.column() == ColumnIndex::Type)
{
const QPixmap* icon = nullptr;
if (AZ::Utils::IsContainerType(typeId) && !AZ::Utils::IsGenericContainerType(typeId))
{
AZStd::vector<AZ::Uuid> dataTypes = AZ::Utils::GetContainedTypes(typeId);
GraphCanvas::StyleManagerRequestBus::EventResult(icon, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetMultiDataTypeIcon, dataTypes);
}
else
{
GraphCanvas::StyleManagerRequestBus::EventResult(icon, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetDataTypeIcon, typeId);
}
if (icon)
{
return *icon;
}
}
else if (index.column() == ColumnIndex::Pinned)
{
AZStd::intrusive_ptr<EditorSettings::ScriptCanvasEditorSettings> settings = AZ::UserSettings::CreateFind<EditorSettings::ScriptCanvasEditorSettings>(AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL);
bool showPin = settings->m_pinnedDataTypes.find(typeId) != settings->m_pinnedDataTypes.end();
if (m_pinningChanges.find(typeId) != m_pinningChanges.end())
{
showPin = !showPin;
}
if (showPin)
{
return m_pinIcon;
}
}
}
break;
case Qt::EditRole:
{
if (index.column() == ColumnIndex::Type)
{
AZStd::string typeString = TranslationHelper::GetSafeTypeName(ScriptCanvas::Data::FromAZType(typeId));
return QString(typeString.c_str());
}
}
break;
default:
break;
}
return QVariant();
}
Qt::ItemFlags DataTypePaletteModel::flags(const QModelIndex&) const
{
return Qt::ItemFlags(
Qt::ItemIsEnabled |
Qt::ItemIsSelectable);
}
QItemSelectionRange DataTypePaletteModel::GetSelectionRangeForRow(int row)
{
return QItemSelectionRange(createIndex(row, 0, nullptr), createIndex(row, columnCount(), nullptr));
}
void DataTypePaletteModel::ClearTypes()
{
layoutAboutToBeChanged();
m_variableTypes.clear();
m_typeNameMapping.clear();
layoutChanged();
}
void DataTypePaletteModel::PopulateVariablePalette(const AZStd::unordered_set< AZ::Uuid >& dataTypes)
{
layoutAboutToBeChanged();
m_variableTypes.reserve(dataTypes.size() + m_variableTypes.size());
for (const AZ::Uuid& typeId : dataTypes)
{
AddDataTypeImpl(typeId);
}
layoutChanged();
}
void DataTypePaletteModel::AddDataType(const AZ::TypeId& typeId)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
AddDataTypeImpl(typeId);
endInsertRows();
}
void DataTypePaletteModel::RemoveDataType(const AZ::TypeId& typeId)
{
QModelIndex index = FindIndexForTypeId(typeId);
if (index.isValid())
{
beginRemoveRows(QModelIndex(), index.row(), index.row());
m_variableTypes.erase(m_variableTypes.begin() + index.row());
auto mapIter = m_typeNameMapping.begin();
while (mapIter != m_typeNameMapping.end())
{
if (mapIter->second == typeId)
{
m_typeNameMapping.erase(mapIter);
break;
}
++mapIter;
}
endRemoveRows();
}
}
AZ::TypeId DataTypePaletteModel::FindTypeIdForIndex(const QModelIndex& index) const
{
AZ::TypeId retVal;
if (index.row() >= 0 && index.row() < m_variableTypes.size())
{
retVal = m_variableTypes[index.row()];
}
return retVal;
}
QModelIndex DataTypePaletteModel::FindIndexForTypeId(const AZ::TypeId& typeId) const
{
int row = 0;
for (; row < m_variableTypes.size(); ++row)
{
if (m_variableTypes[row] == typeId)
{
return index(row, 0);
}
}
return QModelIndex();
}
AZ::TypeId DataTypePaletteModel::FindTypeIdForTypeName(const AZStd::string& typeName) const
{
AZ::TypeId retVal = azrtti_typeid<void>();
AZStd::string lowerName = typeName;
AZStd::to_lower(lowerName.begin(), lowerName.end());
auto mapIter = m_typeNameMapping.find(lowerName);
if (mapIter != m_typeNameMapping.end())
{
retVal = mapIter->second;
}
return retVal;
}
AZStd::string DataTypePaletteModel::FindTypeNameForTypeId(const AZ::TypeId& typeId) const
{
return TranslationHelper::GetSafeTypeName(ScriptCanvas::Data::FromAZType(typeId));
}
void DataTypePaletteModel::TogglePendingPinChange(const AZ::Uuid& azVarType)
{
auto pinningIter = m_pinningChanges.find(azVarType);
if (pinningIter != m_pinningChanges.end())
{
m_pinningChanges.erase(pinningIter);
}
else
{
m_pinningChanges.insert(azVarType);
}
}
const AZStd::unordered_set< AZ::Uuid >& DataTypePaletteModel::GetPendingPinChanges() const
{
return m_pinningChanges;
}
void DataTypePaletteModel::SubmitPendingPinChanges()
{
AZStd::intrusive_ptr<EditorSettings::ScriptCanvasEditorSettings> settings = AZ::UserSettings::CreateFind<EditorSettings::ScriptCanvasEditorSettings>(AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL);
if (settings)
{
for (const AZ::Uuid& azVarType : m_pinningChanges)
{
size_t result = settings->m_pinnedDataTypes.erase(azVarType);
if (result == 0)
{
settings->m_pinnedDataTypes.insert(azVarType);
}
// We don't want to let individual container types exist in our data unless they are pinned.
else if (AZ::Utils::IsContainerType(azVarType) && !AZ::Utils::IsGenericContainerType(azVarType))
{
RemoveDataType(azVarType);
}
}
m_pinningChanges.clear();
}
}
const AZStd::vector<AZ::TypeId>& DataTypePaletteModel::GetVariableTypes() const
{
return m_variableTypes;
}
void DataTypePaletteModel::AddDataTypeImpl(const AZ::TypeId& typeId)
{
AZStd::string lowerName = FindTypeNameForTypeId(typeId);
AZStd::to_lower(lowerName.begin(), lowerName.end());
if (ScriptCanvas::Data::IsNumber(typeId))
{
const auto numberTypeId = azrtti_typeid<ScriptCanvas::Data::NumberType>();
m_variableTypes.emplace_back(numberTypeId);
lowerName = ScriptCanvas::Data::GetName(ScriptCanvas::Data::Type::Number());
AZStd::to_lower(lowerName.begin(), lowerName.end());
m_typeNameMapping[lowerName] = numberTypeId;
}
else
{
m_variableTypes.emplace_back(typeId);
m_typeNameMapping[lowerName] = typeId;
}
}
////////////////////////////////////////
// DataTypePaletteSortFilterProxyModel
////////////////////////////////////////
DataTypePaletteSortFilterProxyModel::DataTypePaletteSortFilterProxyModel(QObject* parent)
: QSortFilterProxyModel(parent)
{
}
bool DataTypePaletteSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
if (m_filter.isEmpty())
{
return true;
}
DataTypePaletteModel* model = qobject_cast<DataTypePaletteModel*>(sourceModel());
if (!model)
{
return false;
}
QModelIndex index = model->index(sourceRow, DataTypePaletteModel::ColumnIndex::Type, sourceParent);
QString test = model->data(index, Qt::DisplayRole).toString();
return (test.lastIndexOf(m_testRegex) >= 0);
}
bool DataTypePaletteSortFilterProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const
{
DataTypePaletteModel* model = qobject_cast<DataTypePaletteModel*>(sourceModel());
if (!model)
{
return false;
}
bool pinnedLeft = false;
AZ::TypeId leftDataType = model->FindTypeIdForIndex(left);
bool pinnedRight = false;
AZ::TypeId rightDataType = model->FindTypeIdForIndex(right);
AZStd::intrusive_ptr<EditorSettings::ScriptCanvasEditorSettings> settings = AZ::UserSettings::CreateFind<EditorSettings::ScriptCanvasEditorSettings>(AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL);
if (settings)
{
pinnedLeft = settings->m_pinnedDataTypes.find(leftDataType) != settings->m_pinnedDataTypes.end();
pinnedRight = settings->m_pinnedDataTypes.find(rightDataType) != settings->m_pinnedDataTypes.end();
}
if (pinnedRight == pinnedLeft)
{
return QSortFilterProxyModel::lessThan(left, right);
}
else if (pinnedRight)
{
return false;
}
else
{
return true;
}
}
void DataTypePaletteSortFilterProxyModel::SetFilter(const QString& filter)
{
m_filter = filter;
m_testRegex = QRegExp(m_filter, Qt::CaseInsensitive);
invalidateFilter();
}
}
#include <Editor/View/Widgets/DataTypePalette/moc_DataTypePaletteModel.cpp>
@@ -0,0 +1,103 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QTableView>
#include <AzCore/Memory/SystemAllocator.h>
#include <ScriptCanvas/Data/Data.h>
#endif
namespace ScriptCanvasEditor
{
class DataTypePaletteModel
: public QAbstractTableModel
{
Q_OBJECT
public:
enum ColumnIndex
{
Pinned,
Type,
Count
};
AZ_CLASS_ALLOCATOR(DataTypePaletteModel, AZ::SystemAllocator, 0);
DataTypePaletteModel(QObject* parent = nullptr);
// QAbstractTableModel
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
////
QItemSelectionRange GetSelectionRangeForRow(int row);
void ClearTypes();
void PopulateVariablePalette(const AZStd::unordered_set< AZ::Uuid >& objectTypes);
void AddDataType(const AZ::TypeId& dataType);
void RemoveDataType(const AZ::TypeId& dataType);
AZ::TypeId FindTypeIdForIndex(const QModelIndex& index) const;
AZ::TypeId FindTypeIdForTypeName(const AZStd::string& typeName) const;
QModelIndex FindIndexForTypeId(const AZ::TypeId& typeId) const;
AZStd::string FindTypeNameForTypeId(const AZ::TypeId& typeId) const;
void TogglePendingPinChange(const AZ::Uuid& azVarType);
const AZStd::unordered_set< AZ::Uuid >& GetPendingPinChanges() const;
void SubmitPendingPinChanges();
const AZStd::vector<AZ::TypeId>& GetVariableTypes() const;
private:
void AddDataTypeImpl(const AZ::TypeId& dataType);
QIcon m_pinIcon;
AZStd::unordered_set< AZ::Uuid > m_pinningChanges;
AZStd::vector<AZ::TypeId> m_variableTypes;
AZStd::unordered_map<AZStd::string, AZ::TypeId> m_typeNameMapping;
};
class DataTypePaletteSortFilterProxyModel
: public QSortFilterProxyModel
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(DataTypePaletteSortFilterProxyModel, AZ::SystemAllocator, 0);
DataTypePaletteSortFilterProxyModel(QObject* parent = nullptr);
// QSortFilterProxyModel
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
////
void SetFilter(const QString& filter);
private:
QString m_filter;
QRegExp m_testRegex;
};
}
@@ -0,0 +1,312 @@
/*
* 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 <QGraphicsView>
#include <QLabel>
#include <QMenu>
#include <QMouseEvent>
#include <QVariant>
#include <QVBoxLayout>
#include "GraphTabBar.h"
#include <ScriptCanvas/Bus/RequestBus.h>
#include <Editor/View/Widgets/CanvasWidget.h>
#include <Editor/QtMetaTypes.h>
#include <ScriptCanvas/Asset/AssetDescription.h>
namespace ScriptCanvasEditor
{
namespace Widget
{
GraphTabBar::GraphTabBar(QWidget* parent /*= nullptr*/)
: AzQtComponents::TabBar(parent)
{
setTabsClosable(true);
setMovable(true);
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
connect(this, &QTabBar::currentChanged, this, &GraphTabBar::currentChangedTab);
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &QTabBar::customContextMenuRequested, this, &GraphTabBar::OnContextMenu);
}
void GraphTabBar::RemoveAllBars()
{
for (int i = count() - 1; i >= 0; --i)
{
Q_EMIT TabCloseNoButton(i);
}
}
void GraphTabBar::SetTabText(int tabIndex, const QString& path, Tracker::ScriptCanvasFileState fileState)
{
if (tabIndex >= 0 && tabIndex < count())
{
const char* fileStateTag = "";
switch (fileState)
{
case Tracker::ScriptCanvasFileState::NEW:
fileStateTag = "^";
break;
case Tracker::ScriptCanvasFileState::MODIFIED:
fileStateTag = "*";
break;
default:
break;
}
setTabText(tabIndex, QString("%1%2").arg(path).arg(fileStateTag));
}
}
void GraphTabBar::tabInserted(int index)
{
AzQtComponents::TabBar::tabInserted(index);
Q_EMIT TabInserted(index);
}
void GraphTabBar::tabRemoved(int index)
{
AzQtComponents::TabBar::tabRemoved(index);
Q_EMIT TabRemoved(index);
}
bool GraphTabBar::SelectTab(const AZ::Data::AssetId& assetId)
{
int tabIndex = FindTab(assetId);
if (-1 != tabIndex)
{
setCurrentIndex(tabIndex);
return true;
}
return false;
}
int GraphTabBar::FindTab(const AZ::Data::AssetId& assetId) const
{
for (int tabIndex = 0; tabIndex < count(); ++tabIndex)
{
QVariant tabDataVariant = tabData(tabIndex);
if (tabDataVariant.isValid())
{
auto tabAssetId = tabDataVariant.value<AZ::Data::AssetId>();
if (tabAssetId == assetId)
{
return tabIndex;
}
}
}
return -1;
}
AZ::Data::AssetId GraphTabBar::FindAssetId(int tabIndex)
{
QVariant dataVariant = tabData(tabIndex);
if (dataVariant.isValid())
{
auto tabAssetId = dataVariant.value<AZ::Data::AssetId>();
return tabAssetId;
}
return AZ::Data::AssetId();
}
void GraphTabBar::AddGraphTab(const AZ::Data::AssetId& assetId)
{
InsertGraphTab(count(), assetId);
}
int GraphTabBar::InsertGraphTab(int tabIndex, const AZ::Data::AssetId& assetId)
{
if (!SelectTab(assetId))
{
AZStd::shared_ptr<ScriptCanvasMemoryAsset> memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetId);
if (memoryAsset)
{
ScriptCanvas::AssetDescription assetDescription = memoryAsset->GetAsset().Get()->GetAssetDescription();
QIcon tabIcon = QIcon(assetDescription.GetIconPathImpl());
int newTabIndex = qobject_cast<AzQtComponents::TabWidget*>(parent())->insertTab(tabIndex, new QWidget(), tabIcon, "");
CanvasWidget* canvasWidget = memoryAsset->CreateView(this);
canvasWidget->SetDefaultBorderColor(assetDescription.GetDisplayColorImpl());
auto fileState = memoryAsset->GetFileState();
AZStd::string tabName;
AzFramework::StringFunc::Path::GetFileName(memoryAsset->GetAbsolutePath().c_str(), tabName);
// For opened graphs we need to use their file assetId
if (memoryAsset->GetFileAssetId().IsValid())
{
setTabData(newTabIndex, QVariant::fromValue(memoryAsset->GetFileAssetId()));
}
else
{
// new graphs will need to use their in-memory assetid which we'll need to update
// upon saving the asset
setTabData(newTabIndex, QVariant::fromValue(memoryAsset->GetId()));
}
SetTabText(newTabIndex, tabName.data(), fileState);
return newTabIndex;
}
}
return -1;
}
void GraphTabBar::currentChangedTab(int index)
{
if (index < 0)
{
return;
}
QVariant tab = tabData(index);
if (!tab.isValid())
{
return;
}
auto assetId = tab.value<AZ::Data::AssetId>();
ScriptCanvasEditor::GeneralRequestBus::Broadcast(&ScriptCanvasEditor::GeneralRequests::OnChangeActiveGraphTab, assetId);
}
void GraphTabBar::CloseTab(int index)
{
if (index >= 0 && index < count())
{
QVariant tab = tabData(index);
if (tab.isValid())
{
auto tabAssetId = tab.value<AZ::Data::AssetId>();
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::ClearView, tabAssetId);
}
qobject_cast<AzQtComponents::TabWidget*>(parent())->removeTab(index);
}
}
void GraphTabBar::OnContextMenu(const QPoint& point)
{
QPoint screenPoint = mapToGlobal(point);
int tabIndex = tabAt(point);
bool hasValidTab = (tabIndex >= 0);
bool isModified = false;
QVariant tab = tabData(tabIndex);
if (tab.isValid())
{
auto tabAssetId = tab.value<AZ::Data::AssetId>();
Tracker::ScriptCanvasFileState fileState;
AssetTrackerRequestBus::BroadcastResult(fileState , &AssetTrackerRequests::GetFileState, tabAssetId);
isModified = fileState == Tracker::ScriptCanvasFileState::NEW || fileState == Tracker::ScriptCanvasFileState::MODIFIED;
}
QMenu menu;
QAction* saveAction = menu.addAction("Save");
saveAction->setEnabled(hasValidTab && isModified);
QAction* closeAction = menu.addAction("Close");
closeAction->setEnabled(hasValidTab);
QAction* closeAllAction = menu.addAction("Close All");
QAction* closeAllButThis = menu.addAction("Close All But This");
closeAllButThis->setEnabled(hasValidTab);
menu.addSeparator();
QAction* fullPathAction = menu.addAction("Copy Source Path To Clipboard");
fullPathAction->setEnabled(hasValidTab);
QAction* action = menu.exec(screenPoint);
if (action)
{
if (action == saveAction)
{
Q_EMIT SaveTab(tabIndex);
}
else if (action == closeAction)
{
tabCloseRequested(tabIndex);
}
else if (action == closeAllAction)
{
Q_EMIT CloseAllTabs();
}
else if (action == closeAllButThis)
{
Q_EMIT CloseAllTabsBut(tabIndex);
}
else if (action == fullPathAction)
{
Q_EMIT CopyPathToClipboard(tabIndex);
}
}
}
void GraphTabBar::mouseReleaseEvent(QMouseEvent* event)
{
if (event->button() == Qt::MidButton)
{
int tabIndex = tabAt(event->localPos().toPoint());
if (tabIndex >= 0)
{
tabCloseRequested(tabIndex);
return;
}
}
AzQtComponents::TabBar::mouseReleaseEvent(event);
}
void GraphTabBar::SetFileState(AZ::Data::AssetId assetId, Tracker::ScriptCanvasFileState fileState)
{
int index = FindTab(assetId);
if (index >= 0 && index < count())
{
QVariant tab = tabData(index);
if (tab.isValid())
{
auto tabAssetId = tab.value<AZ::Data::AssetId>();
AZStd::string tabName;
AssetTrackerRequestBus::BroadcastResult(tabName, &AssetTrackerRequests::GetTabName, tabAssetId);
SetTabText(index, tabName.c_str(), fileState);
}
}
}
#include <Editor/View/Widgets/moc_GraphTabBar.cpp>
}
}
@@ -0,0 +1,98 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzQtComponents/Components/Widgets/TabWidget.h>
#include <QMetaType>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Asset/AssetCommon.h>
#include <Editor/Assets/ScriptCanvasAssetTracker.h>
#endif
class QGraphicsView;
class QVBoxLayout;
namespace ScriptCanvasEditor
{
namespace Widget
{
struct GraphTabMetadata
{
AZ::Data::AssetId m_assetId;
QWidget* m_hostWidget = nullptr;
QString m_tabName;
Tracker::ScriptCanvasFileState m_fileState = Tracker::ScriptCanvasFileState::INVALID;
};
class GraphTabBar
: public AzQtComponents::TabBar
{
Q_OBJECT
public:
GraphTabBar(QWidget* parent = nullptr);
// Adds a new tab to the bar
void AddGraphTab(const AZ::Data::AssetId& assetId);
int InsertGraphTab(int tabIndex, const AZ::Data::AssetId& assetId);
bool SelectTab(const AZ::Data::AssetId& assetId);
int FindTab(const AZ::Data::AssetId& assetId) const;
AZ::Data::AssetId FindAssetId(int tabIndex);
//! Removes all tabs from the bar
void RemoveAllBars();
// Updates the tab at the supplied index with the GraphTabMetadata
// The host widget field of the tabMetadata is not used and will not overwrite the tab data
void SetTabText(int tabIndex, const QString& path, Tracker::ScriptCanvasFileState fileState = Tracker::ScriptCanvasFileState::INVALID);
// Closes a tab and cleans up Metadata
void CloseTab(int index);
void OnContextMenu(const QPoint& point);
void mouseReleaseEvent(QMouseEvent* event) override;
void SetFileState(AZ::Data::AssetId assetId, Tracker::ScriptCanvasFileState fileState);
Q_SIGNALS:
void TabInserted(int index);
void TabRemoved(int index);
// Emits a signal to close the tab which is distinct from pressing the close button the actual tab bar.
// This allows handling of the close tab button being pressed different than the actual closing of the tab.
// Pressing the close tab button will prompt the user to save file in tab if it is modified
void TabCloseNoButton(int index);
void SaveTab(int index);
void CloseAllTabs();
void CloseAllTabsBut(int index);
void CopyPathToClipboard(int index);
protected:
void tabInserted(int index) override;
void tabRemoved(int index) override;
private:
// Called when the selected tab changes
void currentChangedTab(int index);
};
}
}
Q_DECLARE_METATYPE(ScriptCanvasEditor::Widget::GraphTabMetadata);
@@ -0,0 +1,168 @@
/*
* 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 "LogPanel.h"
#include <QDateTime>
#include <QTimer>
#include <QTableView>
#include "Editor/View/Widgets/ui_LogPanel.h"
#include <Editor/View/Dialogs/Settings.h>
#include <AzQtComponents/Components/Widgets/TabWidget.h>
namespace ScriptCanvasEditor
{
namespace Widget
{
LogPanel::LogPanel(QWidget* parent /*= nullptr*/)
: AzToolsFramework::LogPanel::BaseLogPanel(parent)
{
ScriptCanvasEditor::GeneralGraphEventBus::Handler::BusConnect();
}
LogPanel::~LogPanel()
{
ScriptCanvasEditor::GeneralGraphEventBus::Handler::BusDisconnect();
}
void LogPanel::OnBuildGameEntity(const AZStd::string& name, const AZ::EntityId& editGraphId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
{
m_scriptCanvasId = scriptCanvasId;
AZStd::intrusive_ptr<Settings> settings = AZ::UserSettings::CreateFind<Settings>(AZ::Crc32(editGraphId.ToString().c_str()), AZ::UserSettings::CT_LOCAL);
if (settings->m_enableLogging)
{
AzToolsFramework::LogPanel::TabSettings settingsTab(name.c_str(), "Script Canvas", "", true, true, true, true);
AddLogTab(settingsTab);
}
}
QWidget* LogPanel::CreateTab(const AzToolsFramework::LogPanel::TabSettings& settings)
{
return new LogTab(this, m_scriptCanvasId, settings);
}
LogPanelWidget::LogPanelWidget(QWidget* parent)
: AzQtComponents::StyledDockWidget(parent)
, ui(new Ui::LogPanel())
{
ui->setupUi(this);
setWindowTitle(tr("Log"));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setMinimumWidth(200);
setMinimumHeight(40);
ui->layout->addWidget(new LogPanel(this));
}
LogTab::LogTab(QWidget* pParent, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, [[maybe_unused]] const AzToolsFramework::LogPanel::TabSettings& in_settings)
: AzToolsFramework::LogPanel::BaseLogView(pParent)
{
QAction* actionClear = new QAction(tr("Clear"), this);
connect(actionClear, SIGNAL(triggered()), this, SLOT(Clear()));
addAction(actionClear);
m_alreadyQueuedDrainMessage = false;
ConnectModelToView(new AzToolsFramework::LogPanel::RingBufferLogDataModel(m_ptrLogView));
ScriptCanvas::LogNotificationBus::Handler::BusConnect(scriptCanvasId);
Clear();
}
LogTab::~LogTab()
{
ScriptCanvas::LogNotificationBus::Handler::BusDisconnect();
}
void LogTab::LogMessage(const AZStd::string& message)
{
AzToolsFramework::Logging::LogLine line(AZStd::string::format("%s", message.c_str()).c_str(), "Log", AzToolsFramework::Logging::LogLine::TYPE_MESSAGE, QDateTime::currentMSecsSinceEpoch());
m_bufferedLines.push(line);
CommitAddedLines();
}
static int s_delayBetweenTraceprintfUpdates = 250; // milliseconds between pumping the traceprintf messages, lower will eat more performance but be more responsive
void LogTab::CommitAddedLines()
{
bool wasQueued = m_alreadyQueuedDrainMessage.exchange(true, AZStd::memory_order_acq_rel);
if (!wasQueued)
{
QTimer::singleShot(s_delayBetweenTraceprintfUpdates, this, &LogTab::DrainMessages);
}
}
void LogTab::Clear()
{
AzToolsFramework::LogPanel::RingBufferLogDataModel* pModel = qobject_cast<AzToolsFramework::LogPanel::RingBufferLogDataModel*>(m_ptrLogView->model());
if (pModel)
{
pModel->Clear();
}
}
void LogTab::DrainMessages()
{
m_alreadyQueuedDrainMessage = false;
bool wasAtMaxScroll = IsAtMaxScroll();
AzToolsFramework::LogPanel::RingBufferLogDataModel* pModel = ((AzToolsFramework::LogPanel::RingBufferLogDataModel*)(m_ptrLogView->model()));
AzToolsFramework::Logging::LogLine currentLine;
bool openedQuery = false;
bool foundLine = false;
do
{
{
if (m_bufferedLines.empty())
{
foundLine = false;
}
else
{
foundLine = true;
currentLine = AZStd::move(m_bufferedLines.front());
m_bufferedLines.pop();
}
}
if (foundLine)
{
if (!openedQuery)
{
openedQuery = true;
}
pModel->AppendLine(currentLine);
}
} while (foundLine); // keep doing this as long as there's line in the buffer.
if (openedQuery)
{
pModel->CommitAdd();
if (wasAtMaxScroll)
{
m_ptrLogView->scrollToBottom();
}
}
}
#include <Editor/View/Widgets/moc_LogPanel.cpp>
}
}
@@ -0,0 +1,105 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzToolsFramework/UI/Logging/LogPanel_Panel.h>
#include <AzToolsFramework/UI/Logging/LogControl.h>
#include <AzToolsFramework/UI/UICore/TargetSelectorButton.hxx>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <Debugger/Bus.h>
#include <ScriptCanvas/Core/NodeBus.h>
#include <ScriptCanvas/Bus/GraphBus.h>
#endif
namespace AzQtComponents
{
class TabWidget;
}
namespace Ui
{
class LogPanel;
}
namespace ScriptCanvasEditor
{
namespace Widget
{
class LogPanel
: public AzToolsFramework::LogPanel::BaseLogPanel
, ScriptCanvasEditor::GeneralGraphEventBus::Handler
{
Q_OBJECT
public:
LogPanel(QWidget* parent = nullptr);
~LogPanel() override;
protected:
void OnBuildGameEntity(const AZStd::string&, const AZ::EntityId& editGraphId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId) override;
QWidget* CreateTab(const AzToolsFramework::LogPanel::TabSettings& settings) override;
ScriptCanvas::ScriptCanvasId m_scriptCanvasId;
AzQtComponents::TabWidget* pTabWidget;
};
class LogPanelWidget
: public AzQtComponents::StyledDockWidget
{
Q_OBJECT
public:
LogPanelWidget(QWidget* parent = nullptr);
AZStd::unique_ptr<Ui::LogPanel> ui;
};
class LogTab
: public AzToolsFramework::LogPanel::BaseLogView
, ScriptCanvas::LogNotificationBus::Handler
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(LogTab, AZ::SystemAllocator, 0);
LogTab(QWidget* pParent, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, const AzToolsFramework::LogPanel::TabSettings& in_settings);
~LogTab() override;
void LogMessage(const AZStd::string& message) override;
private:
AZStd::queue<AzToolsFramework::Logging::LogLine> m_bufferedLines;
AZStd::atomic_bool m_alreadyQueuedDrainMessage; // we also only drain the queue at the end so that we do bulk inserts instead of one at a time.
void CommitAddedLines();
bool m_alreadyQueuedCommit = false;
private Q_SLOTS:
void DrainMessages();
virtual void Clear();
};
}
}
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LogPanel</class>
<widget class="QDockWidget" name="ScriptCanvasLog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>400</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="windowTitle">
<string>Log</string>
</property>
<widget class="QWidget" name="host">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="layout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</widget>
<customwidgets>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,80 @@
/*
* 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 <Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetDataAggregator.h>
namespace ScriptCanvasEditor
{
///////////////////////////////
// LoggingAssetDataAggregator
///////////////////////////////
LoggingAssetDataAggregator::LoggingAssetDataAggregator(const AZ::Data::AssetId& assetId)
: m_assetId(assetId)
{
}
LoggingAssetDataAggregator::~LoggingAssetDataAggregator()
{
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::AnnotateNodeSignal& /**/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::ExecutionThreadEnd& /*loggableEvent*/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::ExecutionThreadBeginning& /*loggableEvent*/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::GraphActivation& /*loggableEvent*/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::GraphDeactivation& /*loggableEvent*/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::NodeStateChange& loggableEvent)
{
ProcessNodeStateChanged(loggableEvent);
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::InputSignal& loggableEvent)
{
ProcessInputSignal(loggableEvent);
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::OutputDataSignal& loggableEvent)
{
ProcessOutputDataSignal(loggableEvent);
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::OutputSignal& loggableEvent)
{
ProcessOutputSignal(loggableEvent);
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::VariableChange& loggableEvent)
{
ProcessVariableChangedSignal(loggableEvent);
}
}
@@ -0,0 +1,49 @@
/*
* 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 <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#include <ScriptCanvas/Debugger/Logger.h>
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
namespace ScriptCanvasEditor
{
class LoggingAssetDataAggregator
: public LoggingDataAggregator
, public ScriptCanvas::LoggableEventVisitor
{
public:
AZ_CLASS_ALLOCATOR(LoggingAssetDataAggregator, AZ::SystemAllocator, 0);
LoggingAssetDataAggregator(const AZ::Data::AssetId& assetId);
~LoggingAssetDataAggregator() override;
bool CanCaptureData() const override { return false; }
bool IsCapturingData() const override { return false; }
protected:
void Visit(ScriptCanvas::AnnotateNodeSignal&);
void Visit(ScriptCanvas::ExecutionThreadEnd&);
void Visit(ScriptCanvas::ExecutionThreadBeginning&);
void Visit(ScriptCanvas::GraphActivation&);
void Visit(ScriptCanvas::GraphDeactivation&);
void Visit(ScriptCanvas::NodeStateChange&);
void Visit(ScriptCanvas::InputSignal&);
void Visit(ScriptCanvas::OutputDataSignal&);
void Visit(ScriptCanvas::OutputSignal&);
void Visit(ScriptCanvas::VariableChange&);
private:
AZ::Data::AssetId m_assetId;
};
}
@@ -0,0 +1,53 @@
/*
* 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 <Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetWindowSession.h>
namespace ScriptCanvasEditor
{
//////////////////////////////
// LoggingAssetWindowSession
//////////////////////////////
LoggingAssetWindowSession::LoggingAssetWindowSession(const AZ::Data::AssetId& assetId, QWidget* parent)
: LoggingWindowSession(parent)
, m_dataAggregator(assetId)
, m_assetId(assetId)
{
SetDataId(m_dataAggregator.GetDataId());
m_ui->captureButton->setEnabled(false);
RegisterTreeRoot(m_dataAggregator.GetTreeRoot());
}
LoggingAssetWindowSession::~LoggingAssetWindowSession()
{
}
void LoggingAssetWindowSession::OnCaptureButtonPressed()
{
}
void LoggingAssetWindowSession::OnPlaybackButtonPressed()
{
// TODO
}
void LoggingAssetWindowSession::OnOptionsButtonPressed()
{
// TODO
}
#include <Editor/View/Widgets/LoggingPanel/AssetWindowSession/moc_LoggingAssetWindowSession.cpp>
}
@@ -0,0 +1,44 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowSession.h>
#include <Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetDataAggregator.h>
#endif
namespace ScriptCanvasEditor
{
class LoggingAssetWindowSession
: public LoggingWindowSession
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(LoggingAssetWindowSession, AZ::SystemAllocator, 0);
LoggingAssetWindowSession(const AZ::Data::AssetId& assetId, QWidget* parent = nullptr);
~LoggingAssetWindowSession() override;
protected:
void OnCaptureButtonPressed() override;
void OnPlaybackButtonPressed() override;
void OnOptionsButtonPressed() override;
private:
AZ::Data::AssetId m_assetId;
LoggingAssetDataAggregator m_dataAggregator;
};
}
@@ -0,0 +1,396 @@
/*
* 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 <AzCore/std/containers/vector.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h>
#include <ScriptCanvas/Debugger/API.h>
#include <ScriptCanvas/Asset/ExecutionLogAssetBus.h>
namespace ScriptCanvasEditor
{
//////////////////////////////
// LiveLoggingDataAggregator
//////////////////////////////
LiveLoggingDataAggregator::LiveLoggingDataAggregator()
: m_captureType(CaptureType::Editor)
, m_isCapturingData(false)
, m_ignoreRegistrations(false)
{
ScriptCanvas::Debugger::ClientUINotificationBus::Handler::BusConnect();
OnCurrentTargetChanged();
}
LiveLoggingDataAggregator::~LiveLoggingDataAggregator()
{
}
void LiveLoggingDataAggregator::OnCurrentTargetChanged()
{
ResetData();
bool isConnected = false;
ScriptCanvas::Debugger::ClientRequestsBus::BroadcastResult(isConnected, &ScriptCanvas::Debugger::ClientRequests::HasValidConnection);
if (isConnected)
{
EditorLoggingComponentNotificationBus::Handler::BusDisconnect();
if (!ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusIsConnected())
{
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusConnect();
}
bool isSelf = false;
ScriptCanvas::Debugger::ClientRequestsBus::BroadcastResult(isSelf, &ScriptCanvas::Debugger::ClientRequests::IsConnectedToSelf);
if (!isSelf)
{
m_captureType = CaptureType::External;
m_staticRegistrations.clear();
}
}
else
{
if (!EditorLoggingComponentNotificationBus::Handler::BusIsConnected())
{
EditorLoggingComponentNotificationBus::Handler::BusConnect();
}
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusDisconnect();
m_captureType = CaptureType::Editor;
SetupEditorEntities();
}
}
bool LiveLoggingDataAggregator::CanCaptureData() const
{
return true;
}
bool LiveLoggingDataAggregator::IsCapturingData() const
{
return m_isCapturingData;
}
void LiveLoggingDataAggregator::OnEditorScriptCanvasComponentActivated(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_assetId.IsValid())
{
RegisterScriptCanvas(namedEntityId, graphIdentifier);
}
}
void LiveLoggingDataAggregator::OnEditorScriptCanvasComponentDeactivated(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
UnregisterScriptCanvas(namedEntityId, graphIdentifier);
}
void LiveLoggingDataAggregator::OnAssetSwitched(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& newGraphIdentifier, const ScriptCanvas::GraphIdentifier& oldGraphIdentifier)
{
if (newGraphIdentifier == oldGraphIdentifier)
{
return;
}
if (newGraphIdentifier.m_assetId.IsValid())
{
RegisterScriptCanvas(namedEntityId, newGraphIdentifier);
}
UnregisterScriptCanvas(namedEntityId, oldGraphIdentifier);
RemoveStaticRegistration(namedEntityId, oldGraphIdentifier);
}
void LiveLoggingDataAggregator::Connected([[maybe_unused]] const ScriptCanvas::Debugger::Target& target)
{
AZStd::lock(m_notificationMutex);
SetupExternalEntities();
}
void LiveLoggingDataAggregator::GraphActivated(const ScriptCanvas::GraphActivation& activationSignal)
{
AZStd::lock(m_notificationMutex);
RegisterScriptCanvas(activationSignal.m_runtimeEntity, activationSignal.m_graphIdentifier);
RegisterEntityName(activationSignal.m_runtimeEntity, activationSignal.m_runtimeEntity.GetName());
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, activationSignal.m_entityIsObserved, activationSignal.m_runtimeEntity, activationSignal.m_graphIdentifier);
}
void LiveLoggingDataAggregator::GraphDeactivated(const ScriptCanvas::GraphDeactivation& deactivationSignal)
{
AZStd::lock(m_notificationMutex);
UnregisterScriptCanvas(deactivationSignal.m_runtimeEntity, deactivationSignal.m_graphIdentifier);
}
void LiveLoggingDataAggregator::NodeStateChanged(const ScriptCanvas::NodeStateChange& nodeStateChangeSignal)
{
AZStd::lock(m_notificationMutex);
ProcessNodeStateChanged(nodeStateChangeSignal);
}
void LiveLoggingDataAggregator::SignaledInput(const ScriptCanvas::InputSignal& inputSignal)
{
AZStd::lock(m_notificationMutex);
ProcessInputSignal(inputSignal);
}
void LiveLoggingDataAggregator::SignaledOutput(const ScriptCanvas::OutputSignal& outputSignal)
{
AZStd::lock(m_notificationMutex);
ProcessOutputSignal(outputSignal);
}
void LiveLoggingDataAggregator::SignaledDataOutput(const ScriptCanvas::OutputDataSignal& outputDataSignal)
{
AZStd::lock(m_notificationMutex);
ProcessOutputDataSignal(outputDataSignal);
}
void LiveLoggingDataAggregator::AnnotateNode(const ScriptCanvas::AnnotateNodeSignal& annotateNode)
{
AZStd::lock(m_notificationMutex);
ProcessAnnotateNode(annotateNode);
}
void LiveLoggingDataAggregator::VariableChanged(const ScriptCanvas::VariableChange& variableChangeSignal)
{
AZStd::lock(m_notificationMutex);
ProcessVariableChangedSignal(variableChangeSignal);
}
void LiveLoggingDataAggregator::GetActiveEntitiesResult(const ScriptCanvas::ActiveEntityStatusMap& activeEntities)
{
AZStd::lock(m_notificationMutex);
m_ignoreRegistrations = true;
for (const auto& activeEntityPair : activeEntities)
{
const AZ::NamedEntityId& namedEntityId = activeEntityPair.first;
RegisterEntityName(namedEntityId, namedEntityId.GetName());
const auto& activeEntityStatus = activeEntityPair.second;
for (const auto& activeGraphStatus : activeEntityStatus.m_activeGraphs)
{
RegisterScriptCanvas(namedEntityId, activeGraphStatus.first);
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, activeGraphStatus.second.m_isObserved, namedEntityId, activeGraphStatus.first);
}
}
m_ignoreRegistrations = false;
}
const AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier>& LiveLoggingDataAggregator::GetStaticRegistrations() const
{
return m_staticRegistrations;
}
void LiveLoggingDataAggregator::OnRegistrationEnabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (m_ignoreRegistrations)
{
return;
}
if (IsCapturingData() || m_captureType == External)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::AddGraphLoggingTarget, graphIdentifier.m_assetId);
}
else
{
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::AddEntityLoggingTarget, namedEntityId, graphIdentifier);
bool gotResult = false;
AZ::EntityId editorId;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(gotResult, &AzToolsFramework::EditorEntityContextRequests::MapRuntimeIdToEditorId, namedEntityId, editorId);
if (gotResult)
{
AZ::NamedEntityId namedEditorId(editorId, namedEntityId.GetName());
AddStaticRegistration(namedEditorId, graphIdentifier);
}
}
return;
}
AddStaticRegistration(namedEntityId, graphIdentifier);
}
void LiveLoggingDataAggregator::OnRegistrationDisabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (m_ignoreRegistrations)
{
return;
}
if (IsCapturingData() || m_captureType == External)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::RemoveGraphLoggingTarget, graphIdentifier.m_assetId);
}
else
{
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::RemoveEntityLoggingTarget, namedEntityId, graphIdentifier);
if (m_captureType == Editor)
{
bool gotResult = false;
AZ::EntityId editorId;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(gotResult, &AzToolsFramework::EditorEntityContextRequests::MapRuntimeIdToEditorId, namedEntityId, editorId);
if (gotResult)
{
AZ::NamedEntityId namedEditorId(editorId, namedEntityId.GetName());
RemoveStaticRegistration(namedEditorId, graphIdentifier);
}
}
}
return;
}
RemoveStaticRegistration(namedEntityId, graphIdentifier);
}
void LiveLoggingDataAggregator::AddStaticRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId
|| m_captureType != Editor)
{
return;
}
bool registerEvent = true;
auto mapRange = m_staticRegistrations.equal_range(namedEntityId);
for (auto mapIter = mapRange.first; mapIter != mapRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
registerEvent = false;
break;
}
}
if (registerEvent)
{
m_staticRegistrations.insert(AZStd::make_pair(namedEntityId, graphIdentifier));
}
}
void LiveLoggingDataAggregator::RemoveStaticRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId
|| m_captureType != Editor)
{
return;
}
auto mapRange = m_staticRegistrations.equal_range(namedEntityId);
for (auto mapIter = mapRange.first; mapIter != mapRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
m_staticRegistrations.erase(mapIter);
break;
}
}
}
void LiveLoggingDataAggregator::SetupEditorEntities()
{
m_ignoreRegistrations = true;
EditorScriptCanvasComponentLoggingBus::EnumerateHandlers([this](EditorScriptCanvasComponentLogging* loggingComponent)
{
ScriptCanvas::GraphIdentifier graphIdentifier = loggingComponent->GetGraphIdentifier();
if (graphIdentifier.m_assetId.IsValid())
{
RegisterScriptCanvas(loggingComponent->FindNamedEntityId(), graphIdentifier);
}
return true;
});
for (const auto& mapPair : m_staticRegistrations)
{
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, true, mapPair.first, mapPair.second);
}
m_ignoreRegistrations = false;
}
void LiveLoggingDataAggregator::SetupExternalEntities()
{
ScriptCanvas::Debugger::ClientRequestsBus::Broadcast(&ScriptCanvas::Debugger::ClientRequests::GetActiveEntities);
}
void LiveLoggingDataAggregator::StartCaptureData()
{
AZStd::lock(m_notificationMutex);
m_isCapturingData = true;
ResetLog();
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusConnect();
}
void LiveLoggingDataAggregator::StopCaptureData()
{
AZStd::lock(m_notificationMutex);
m_isCapturingData = false;
ResetData();
const AZStd::string name = AZStd::string::format("ScriptCanvasLog_%s", AZStd::to_string(AZStd::GetTimeUTCMilliSecond()).data());
ScriptCanvas::ExecutionLogAssetEBus::Broadcast(&ScriptCanvas::ExecutionLogAssetBus::SaveToRelativePath, name);
if (m_captureType == CaptureType::Editor)
{
bool isDesiredTargetConnected = false;
AzFramework::TargetManager::Bus::BroadcastResult(isDesiredTargetConnected, &AzFramework::TargetManager::IsDesiredTargetOnline);
if (isDesiredTargetConnected)
{
SetupExternalEntities();
}
else
{
SetupEditorEntities();
}
}
else
{
SetupExternalEntities();
}
ScriptCanvas::ExecutionLogAssetEBus::Broadcast(&ScriptCanvas::ExecutionLogAssetBus::ClearLog);
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusDisconnect();
}
}
@@ -0,0 +1,99 @@
/*
* 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 <AzFramework/TargetManagement/TargetManagementAPI.h>
#include <Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#include <ScriptCanvas/Debugger/Bus.h>
#include <ScriptCanvas/Debugger/Logger.h>
namespace ScriptCanvasEditor
{
class LiveLoggingDataAggregator
: public LoggingDataAggregator
, public EditorLoggingComponentNotificationBus::Handler
, public ScriptCanvas::Debugger::ServiceNotificationsBus::Handler
, public ScriptCanvas::Debugger::ClientUINotificationBus::Handler
{
enum CaptureType
{
Editor,
External
};
public:
AZ_CLASS_ALLOCATOR(LiveLoggingDataAggregator, AZ::SystemAllocator, 0);
LiveLoggingDataAggregator();
~LiveLoggingDataAggregator();
// ClientUINotificationBus
void OnCurrentTargetChanged() override;
////
bool CanCaptureData() const;
bool IsCapturingData() const;
void StartCaptureData();
void StopCaptureData();
// EditorLoggingComponentNotifications
void OnEditorScriptCanvasComponentActivated(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void OnEditorScriptCanvasComponentDeactivated(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void OnAssetSwitched(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& newAssetId, const ScriptCanvas::GraphIdentifier& oldAssetId) override;
////
// ServiceNotifications
//// Logging Notifications
void Connected(const ScriptCanvas::Debugger::Target& target) override;
void GraphActivated(const ScriptCanvas::GraphActivation& activatedSignal) override;
void GraphDeactivated(const ScriptCanvas::GraphDeactivation& deactivatedSignal) override;
void NodeStateChanged(const ScriptCanvas::NodeStateChange& stateChange) override;
void SignaledInput(const ScriptCanvas::InputSignal& inputSignal) override;
void SignaledOutput(const ScriptCanvas::OutputSignal& outputSignal) override;
void SignaledDataOutput(const ScriptCanvas::OutputDataSignal&) override;
void AnnotateNode(const ScriptCanvas::AnnotateNodeSignal& annotateNode) override;
void VariableChanged(const ScriptCanvas::VariableChange& variableChanged) override;
//// Result Methods
void GetActiveEntitiesResult(const ScriptCanvas::ActiveEntityStatusMap& activeEntityMap) override;
////
const AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier>& GetStaticRegistrations() const;
protected:
void OnRegistrationEnabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void OnRegistrationDisabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
private:
void AddStaticRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void RemoveStaticRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void SetupEditorEntities();
void SetupExternalEntities();
CaptureType m_captureType;
bool m_isCapturingData;
bool m_ignoreRegistrations;
AZStd::recursive_mutex m_notificationMutex;
ScriptCanvas::Debugger::Logger m_logger;
AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier> m_staticRegistrations;
};
}
@@ -0,0 +1,549 @@
/*
* 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 <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <EditorCoreAPI.h>
#include <IEditor.h>
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingWindowSession.h>
namespace ScriptCanvasEditor
{
///////////////////////
// TargetManagerModel
///////////////////////
TargetManagerModel::TargetManagerModel()
{
AzFramework::TargetInfo editorTargetInfo(0, "Editor");
m_targetInfo.push_back(editorTargetInfo);
AzFramework::TargetManager::Bus::BroadcastResult(m_selfInfo, &AzFramework::TargetManager::GetMyTargetInfo);
ScrapeTargetInfo();
}
int TargetManagerModel::rowCount([[maybe_unused]] const QModelIndex& parent) const
{
return static_cast<int>(m_targetInfo.size());
}
QVariant TargetManagerModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
switch (role)
{
case Qt::DisplayRole:
{
const AzFramework::TargetInfo& targetInfo = m_targetInfo[index.row()];
if (index.row() > 0)
{
return QString("%1 (%2)").arg(targetInfo.GetDisplayName(), QString::number(targetInfo.GetPersistentId(), 16));
}
else
{
return QString(targetInfo.GetDisplayName());
}
}
break;
default:
break;
}
return QVariant();
}
void TargetManagerModel::TargetJoinedNetwork(AzFramework::TargetInfo info)
{
if (!info.IsIdentityEqualTo(m_selfInfo))
{
int element = GetRowForTarget(info.GetPersistentId());
if (element < 0)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_targetInfo.push_back(info);
endInsertRows();
}
}
else
{
ScrapeTargetInfo();
}
}
void TargetManagerModel::TargetLeftNetwork(AzFramework::TargetInfo info)
{
int element = GetRowForTarget(info.GetPersistentId());
// 0 is reserved for our fake Editor one.
// And we don't want to remove it.
if (element > 0)
{
beginRemoveRows(QModelIndex(), element, element);
m_targetInfo.erase(m_targetInfo.begin() + element);
endRemoveRows();
}
}
AzFramework::TargetInfo TargetManagerModel::FindTargetInfoForRow(int row)
{
if (row < 0 && row >= m_targetInfo.size())
{
return AzFramework::TargetInfo();
}
return m_targetInfo[row];
}
int TargetManagerModel::GetRowForTarget(AZ::u32 targetId)
{
for (size_t i = 0; i < m_targetInfo.size(); ++i)
{
if (m_targetInfo[i].GetPersistentId() == targetId)
{
return static_cast<int>(i);
}
}
return -1;
}
void TargetManagerModel::ScrapeTargetInfo()
{
AzFramework::TargetContainer targets;
AzFramework::TargetManager::Bus::Broadcast(&AzFramework::TargetManager::EnumTargetInfos, targets);
for (const auto& targetPair : targets)
{
if (!targetPair.second.IsIdentityEqualTo(m_selfInfo))
{
m_targetInfo.push_back(targetPair.second);
}
}
}
////////////////////////////
// LiveLoggingUserSettings
////////////////////////////
AZStd::intrusive_ptr<LiveLoggingUserSettings> LiveLoggingUserSettings::FindSettingsInstance()
{
return AZ::UserSettings::CreateFind<LiveLoggingUserSettings>(AZ_CRC("ScriptCanvas::LiveLoggingUserSettings", 0xc79efe7b), AZ::UserSettings::CT_LOCAL);
}
void LiveLoggingUserSettings::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<LiveLoggingUserSettings>()
->Version(1)
->Field("AutoCapturing", &LiveLoggingUserSettings::m_isAutoCaptureEnabled)
->Field("LiveUpdating", &LiveLoggingUserSettings::m_enableLiveUpdates)
;
}
}
void LiveLoggingUserSettings::SetAutoCaptureEnabled(bool enabled)
{
m_isAutoCaptureEnabled = enabled;
}
bool LiveLoggingUserSettings::IsAutoCaptureEnabled() const
{
return m_isAutoCaptureEnabled;
}
void LiveLoggingUserSettings::SetLiveUpdates(bool enabled)
{
m_enableLiveUpdates = enabled;
}
bool LiveLoggingUserSettings::IsLiveUpdating() const
{
return m_enableLiveUpdates;
}
/////////////////////////////
// LiveLoggingWindowSession
/////////////////////////////
LiveLoggingWindowSession::LiveLoggingWindowSession(QWidget* parent)
: LoggingWindowSession(parent)
, m_startedSession(false)
, m_encodeStaticEntities(false)
, m_isCapturing(false)
{
AzFramework::TargetManagerClient::Bus::Handler::BusConnect();
m_targetManagerModel = aznew TargetManagerModel();
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
m_ui->targetSelector->setModel(m_targetManagerModel);
}
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusConnect();
SetDataId(m_liveDataAggregator.GetDataId());
RegisterTreeRoot(m_liveDataAggregator.GetTreeRoot());
m_userSettings = LiveLoggingUserSettings::FindSettingsInstance();
if (!m_userSettings->IsLiveUpdating())
{
m_liveDataAggregator.GetTreeRoot()->SetUpdatePolicy(DebugLogRootItem::UpdatePolicy::SingleTime);
}
else
{
m_liveDataAggregator.GetTreeRoot()->SetUpdatePolicy(DebugLogRootItem::UpdatePolicy::RealTime);
}
// Despite being apart of the base menu for now, the LiveLoggingWindow is the only one that needs to utilize these buttons.
// Going to control them from here.
m_ui->liveUpdatesToggle->setChecked(m_userSettings->IsLiveUpdating());
QObject::connect(m_ui->liveUpdatesToggle, &QToolButton::toggled, this, &LiveLoggingWindowSession::OnLiveUpdateToggled);
m_ui->autoCaptureToggle->setChecked(m_userSettings->IsAutoCaptureEnabled());
QObject::connect(m_ui->autoCaptureToggle, &QToolButton::toggled, this, &LiveLoggingWindowSession::OnAutoCaptureToggled);
}
LiveLoggingWindowSession::~LiveLoggingWindowSession()
{
AzFramework::TargetManagerClient::Bus::Handler::BusDisconnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusDisconnect();
}
void LiveLoggingWindowSession::DesiredTargetChanged(AZ::u32 newId, [[maybe_unused]] AZ::u32 oldId)
{
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
int row = m_targetManagerModel->GetRowForTarget(newId);
if (row < 0)
{
m_ui->targetSelector->setCurrentIndex(0);
}
else
{
m_ui->targetSelector->setCurrentIndex(row);
}
}
}
void LiveLoggingWindowSession::DesiredTargetConnected(bool connected)
{
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
bool useFallback = !connected;
if (connected)
{
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
AzFramework::TargetInfo desiredInfo;
AzFramework::TargetManager::Bus::BroadcastResult(desiredInfo, &AzFramework::TargetManager::GetDesiredTarget);
if (desiredInfo.IsValid() && !desiredInfo.IsSelf())
{
int index = m_targetManagerModel->GetRowForTarget(desiredInfo.GetPersistentId());
if (index > 0)
{
m_ui->targetSelector->setCurrentIndex(index);
}
}
else
{
useFallback = true;
}
}
else if (m_isCapturing)
{
SetIsCapturing(false);
}
if (useFallback)
{
if (!AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusIsConnected())
{
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
}
m_ui->targetSelector->setCurrentIndex(0);
}
}
}
void LiveLoggingWindowSession::TargetJoinedNetwork(AzFramework::TargetInfo info)
{
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
m_targetManagerModel->TargetJoinedNetwork(info);
}
}
void LiveLoggingWindowSession::TargetLeftNetwork(AzFramework::TargetInfo info)
{
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
m_targetManagerModel->TargetLeftNetwork(info);
}
}
void LiveLoggingWindowSession::OnStartPlayInEditorBegin()
{
if (isVisible())
{
m_encodeStaticEntities = true;
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::StartEditorSession);
if ((m_userSettings->IsAutoCaptureEnabled()) || m_startedSession)
{
SetIsCapturing(true);
}
}
}
void LiveLoggingWindowSession::OnStopPlayInEditor()
{
if (isVisible())
{
SetIsCapturing(false);
m_startedSession = false;
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::StopEditorSession);
m_encodeStaticEntities = false;
}
}
void LiveLoggingWindowSession::Connected([[maybe_unused]] const ScriptCanvas::Debugger::Target& target)
{
if (m_userSettings->IsAutoCaptureEnabled() && isVisible())
{
SetIsCapturing(true);
}
}
void LiveLoggingWindowSession::OnCaptureButtonPressed()
{
bool isSelfTarget = false;
ScriptCanvas::Debugger::ClientRequestsBus::BroadcastResult(isSelfTarget, &ScriptCanvas::Debugger::ClientRequests::IsConnectedToSelf);
if (isSelfTarget)
{
if (!m_startedSession)
{
bool isRunningGame = false;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(isRunningGame, &AzToolsFramework::EditorEntityContextRequests::IsEditorRunningGame);
if (!isRunningGame)
{
m_startedSession = true;
GetIEditor()->SetInGameMode(true);
return;
}
}
else
{
GetIEditor()->SetInGameMode(false);
return;
}
}
SetIsCapturing(!m_isCapturing);
}
void LiveLoggingWindowSession::OnPlaybackButtonPressed()
{
// Nothing to do in the LiveLoggingWindowSession
}
void LiveLoggingWindowSession::OnOptionsButtonPressed()
{
QPoint point = QCursor::pos();
QMenu optionsMenu;
QAction* autoCaptureAction = optionsMenu.addAction("Auto Capture");
autoCaptureAction->setCheckable(true);
autoCaptureAction->setChecked(m_userSettings->IsAutoCaptureEnabled());
QObject::connect(autoCaptureAction, &QAction::toggled, this, &LiveLoggingWindowSession::OnAutoCaptureToggled);
QAction* liveUpdateAction = optionsMenu.addAction("Live Updates");
liveUpdateAction->setCheckable(true);
liveUpdateAction->setChecked(m_userSettings->IsLiveUpdating());
QObject::connect(liveUpdateAction, &QAction::toggled, this, &LiveLoggingWindowSession::OnLiveUpdateToggled);
optionsMenu.exec(point);
}
void LiveLoggingWindowSession::OnTargetChanged(int index)
{
// Special case out the editor
if (index == 0)
{
AzFramework::TargetManager::Bus::Broadcast(&AzFramework::TargetManager::SetDesiredTarget, 0);
}
else
{
AzFramework::TargetInfo info = m_targetManagerModel->FindTargetInfoForRow(index);
if (info.IsValid())
{
AzFramework::TargetManager::Bus::Broadcast(&AzFramework::TargetManager::SetDesiredTarget, info.GetNetworkId());
}
}
}
void LiveLoggingWindowSession::OnAutoCaptureToggled(bool checked)
{
m_userSettings->SetAutoCaptureEnabled(checked);
}
void LiveLoggingWindowSession::OnLiveUpdateToggled(bool checked)
{
m_userSettings->SetLiveUpdates(checked);
if (!m_userSettings->IsLiveUpdating())
{
m_liveDataAggregator.GetTreeRoot()->SetUpdatePolicy(DebugLogRootItem::UpdatePolicy::SingleTime);
}
else
{
// If we enable this we want to update the current display.
m_liveDataAggregator.GetTreeRoot()->RedoLayout();
m_liveDataAggregator.GetTreeRoot()->SetUpdatePolicy(DebugLogRootItem::UpdatePolicy::RealTime);
}
}
void LiveLoggingWindowSession::StartDataCapture()
{
ScriptCanvas::Debugger::ScriptTarget captureInfo;
ConfigureScriptTarget(captureInfo);
m_liveDataAggregator.StartCaptureData();
m_ui->captureButton->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/capture_live.png"));
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::StartLogging, captureInfo);
}
void LiveLoggingWindowSession::StopDataCapture()
{
m_liveDataAggregator.StopCaptureData();
m_ui->captureButton->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/capture_offline.png"));
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::StopLogging);
if (!m_userSettings->IsLiveUpdating())
{
m_liveDataAggregator.GetTreeRoot()->RedoLayout();
}
}
void LiveLoggingWindowSession::ConfigureScriptTarget(ScriptCanvas::Debugger::ScriptTarget& captureInfo)
{
if (m_encodeStaticEntities)
{
const auto& staticRegistrations = m_liveDataAggregator.GetStaticRegistrations();
for (const auto& registrationPair : staticRegistrations)
{
bool gotResult = false;
AZ::EntityId runtimeId;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(gotResult, &AzToolsFramework::EditorEntityContextRequests::MapEditorIdToRuntimeId, registrationPair.first, runtimeId);
if (runtimeId.IsValid())
{
auto entityIter = captureInfo.m_entities.find(runtimeId);
if (entityIter == captureInfo.m_entities.end())
{
auto insertResult = captureInfo.m_entities.insert(AZStd::make_pair(runtimeId, AZStd::unordered_set< ScriptCanvas::GraphIdentifier >()));
entityIter = insertResult.first;
}
entityIter->second.insert(registrationPair.second);
m_liveDataAggregator.RegisterEntityName(runtimeId, registrationPair.first.GetName());
}
else
{
auto insertResult = captureInfo.m_staticEntities.insert(registrationPair.first);
insertResult.first->second.insert(registrationPair.second);
}
}
}
const LoggingEntityMap& registrationMap = m_liveDataAggregator.GetLoggingEntityMap();
for (const auto& registrationPair : registrationMap)
{
auto entityIter = captureInfo.m_entities.find(registrationPair.first);
if (entityIter == captureInfo.m_entities.end())
{
auto insertResult = captureInfo.m_entities.insert(AZStd::make_pair(registrationPair.first, AZStd::unordered_set< ScriptCanvas::GraphIdentifier >()));
entityIter = insertResult.first;
}
entityIter->second.insert(registrationPair.second);
}
const LoggingAssetSet& registrationSet = m_liveDataAggregator.GetLoggingAssetSet();
for (const auto& graphIdentifier : registrationSet)
{
captureInfo.m_graphs.insert(graphIdentifier.m_assetId);
}
}
void LiveLoggingWindowSession::SetIsCapturing(bool isCapturing)
{
if (isCapturing != m_isCapturing)
{
m_isCapturing = isCapturing;
if (m_isCapturing)
{
StartDataCapture();
}
else
{
StopDataCapture();
}
}
}
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/moc_LiveLoggingWindowSession.cpp>
}
@@ -0,0 +1,143 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QAbstractListModel>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzFramework/TargetManagement/TargetManagementAPI.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowSession.h>
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h>
#endif
namespace ScriptCanvasEditor
{
class TargetManagerModel
: public QAbstractListModel
{
public:
AZ_CLASS_ALLOCATOR(TargetManagerModel, AZ::SystemAllocator, 0);
TargetManagerModel();
// QAbstarctItemModel
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
////
void TargetJoinedNetwork(AzFramework::TargetInfo info);
void TargetLeftNetwork(AzFramework::TargetInfo info);
AzFramework::TargetInfo FindTargetInfoForRow(int row);
int GetRowForTarget(AZ::u32 targetId);
private:
void ScrapeTargetInfo();
AzFramework::TargetInfo m_selfInfo;
AZStd::vector< AzFramework::TargetInfo > m_targetInfo;
};
class LiveLoggingUserSettings
: public AZ::UserSettings
{
public:
static AZStd::intrusive_ptr<LiveLoggingUserSettings> FindSettingsInstance();
AZ_RTTI(LiveLoggingUserSettings, "{2E32C949-5766-480D-B569-781BE9166B2E}", AZ::UserSettings);
AZ_CLASS_ALLOCATOR(LiveLoggingUserSettings, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
LiveLoggingUserSettings() = default;
void SetAutoCaptureEnabled(bool enabled);
bool IsAutoCaptureEnabled() const;
void SetLiveUpdates(bool enabled);
bool IsLiveUpdating() const;
private:
bool m_isAutoCaptureEnabled = true;
bool m_enableLiveUpdates = true;
};
class LiveLoggingWindowSession
: public LoggingWindowSession
, public AzFramework::TargetManagerClient::Bus::Handler
, public AzToolsFramework::EditorEntityContextNotificationBus::Handler
, public ScriptCanvas::Debugger::ServiceNotificationsBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(LiveLoggingWindowSession, AZ::SystemAllocator, 0);
LiveLoggingWindowSession(QWidget* parent = nullptr);
~LiveLoggingWindowSession() override;
// AzFramework::TargetManagerClient
void DesiredTargetChanged(AZ::u32 newId, AZ::u32 oldId) override;
void DesiredTargetConnected(bool connected) override;
void TargetJoinedNetwork(AzFramework::TargetInfo info) override;
void TargetLeftNetwork(AzFramework::TargetInfo info) override;
////
// AzToolsFramework::EditorEntityContextNotificationBus::Handler
void OnStartPlayInEditorBegin();
void OnStopPlayInEditor();
////
// ScriptCavnas::Debugger::ServiceNotificationsBus
void Connected(const ScriptCanvas::Debugger::Target& target) override;
////
protected:
void OnCaptureButtonPressed() override;
void OnPlaybackButtonPressed() override;
void OnOptionsButtonPressed() override;
void OnTargetChanged(int currentIndex) override;
private:
void OnAutoCaptureToggled(bool checked);
void OnLiveUpdateToggled(bool checked);
void StartDataCapture();
void StopDataCapture();
void ConfigureScriptTarget(ScriptCanvas::Debugger::ScriptTarget& captureInfo);
void SetIsCapturing(bool isCapturing);
TargetManagerModel* m_targetManagerModel;
bool m_startedSession;
bool m_encodeStaticEntities;
bool m_isCapturing;
LiveLoggingDataAggregator m_liveDataAggregator;
ScriptCanvas::Debugger::Target m_targetConfiguration;
AZStd::intrusive_ptr<LiveLoggingUserSettings> m_userSettings;
};
}
@@ -0,0 +1,423 @@
/*
* 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 <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
namespace ScriptCanvasEditor
{
//////////////////////////
// LoggingDataAggregator
//////////////////////////
LoggingDataAggregator::LoggingDataAggregator()
: m_id(AZ::Entity::MakeId())
, m_ignoreRegistrations(false)
, m_hasAnchor(false)
, m_anchorTimeStamp(0)
{
LoggingDataRequestBus::Handler::BusConnect(m_id);
m_debugLogRoot = aznew DebugLogRootItem();
}
LoggingDataAggregator::~LoggingDataAggregator()
{
}
const LoggingDataId& LoggingDataAggregator::GetDataId() const
{
return m_id;
}
const LoggingDataAggregator* LoggingDataAggregator::FindLoggingData() const
{
return this;
}
void LoggingDataAggregator::EnableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (m_ignoreRegistrations)
{
return;
}
bool signalAddition = false;
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
auto insertResult = m_loggedAssetSet.insert(graphIdentifier);
signalAddition = insertResult.second;
}
else
{
signalAddition = true;
auto equalRange = m_loggingEntityMapping.equal_range(namedEntityId);
for (auto mapIter = equalRange.first; mapIter != equalRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
signalAddition = false;
break;
}
}
if (signalAddition)
{
m_loggingEntityMapping.insert(AZStd::make_pair(namedEntityId, graphIdentifier));
}
}
if (signalAddition)
{
m_ignoreRegistrations = true;
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, true, namedEntityId, graphIdentifier);
m_ignoreRegistrations = false;
OnRegistrationEnabled(namedEntityId, graphIdentifier);
}
}
void LoggingDataAggregator::DisableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (m_ignoreRegistrations)
{
return;
}
bool signalErase = false;
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
size_t eraseCount = m_loggedAssetSet.erase(graphIdentifier);
signalErase = eraseCount > 0;
}
else
{
auto equalRange = m_loggingEntityMapping.equal_range(namedEntityId);
for (auto mapIter = equalRange.first; mapIter != equalRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
signalErase = true;
m_loggingEntityMapping.erase(mapIter);
break;
}
}
}
if (signalErase)
{
m_ignoreRegistrations = true;
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, false, namedEntityId, graphIdentifier);
m_ignoreRegistrations = false;
OnRegistrationDisabled(namedEntityId, graphIdentifier);
}
}
AZ::NamedEntityId LoggingDataAggregator::FindNamedEntityId(const AZ::EntityId& entityId)
{
auto cacheIter = m_entityNameCache.find(entityId);
if (cacheIter != m_entityNameCache.end())
{
return AZ::NamedEntityId(entityId, cacheIter->second);
}
return AZ::NamedEntityId(entityId, "<unknown>");
}
const EntityGraphRegistrationMap& LoggingDataAggregator::GetEntityGraphRegistrationMap() const
{
return m_registrationMap;
}
const LoggingEntityMap& LoggingDataAggregator::GetLoggingEntityMap() const
{
return m_loggingEntityMapping;
}
const LoggingAssetSet& LoggingDataAggregator::GetLoggingAssetSet() const
{
return m_loggedAssetSet;
}
void LoggingDataAggregator::ProcessSignal([[maybe_unused]] const ScriptCanvas::Signal& signal)
{
//GraphIdentifier identifier;
//identifier.m_entityId = signal.m_runtimeEntity;
//identifier.m_assetId = signal.m_graphCount.m_assetId;
//identifier.m_sequenceId = signal.m_graphCount.m_count;
//auto aggregateIter = m_lastAggregateItemMap.find(identifier);
//if (aggregateIter != m_lastAggregateItemMap.end())
//{
// /*
// if (aggregateIter->second->TryProcessSignal(signal))
// {
// return;
// }
// */
// m_lastAggregateItemMap.erase(identifier);
//}
// Signal events are ambiguous on their own. Will need a secondary source of information to be able to disambiguate them.
}
void LoggingDataAggregator::ProcessNodeStateChanged([[maybe_unused]] const ScriptCanvas::NodeStateChange& stateChangeSignal)
{
}
void LoggingDataAggregator::ProcessInputSignal(const ScriptCanvas::InputSignal& inputSignal)
{
if (!m_hasAnchor)
{
m_hasAnchor = true;
m_anchorTimeStamp = inputSignal.GetTimestamp();
}
// For every input we always want to make a new element.
ScriptCanvas::Timestamp relativeTimeStamp = inputSignal.GetTimestamp() - m_anchorTimeStamp;
ExecutionLogTreeItem* treeItem = m_debugLogRoot->CreateExecutionItem(GetDataId(), inputSignal.m_nodeType, inputSignal, inputSignal.m_endpoint.GetNamedNodeId());
m_lastAggregateItemMap[inputSignal] = treeItem;
treeItem->RegisterExecutionInput(ScriptCanvas::Endpoint(), inputSignal.m_endpoint.GetSlotId(), inputSignal.m_endpoint.GetSlotName(), AZStd::chrono::milliseconds(relativeTimeStamp));
for (auto dataMap : inputSignal.m_data)
{
AZStd::string valueString = dataMap.second.m_datum.ToString();
treeItem->RegisterDataInput(ScriptCanvas::Endpoint(), dataMap.first, dataMap.first.m_name, valueString, GetTreeRoot()->GetUpdatePolicy() == DebugLogRootItem::UpdatePolicy::RealTime);
}
}
void LoggingDataAggregator::ProcessOutputSignal(const ScriptCanvas::OutputSignal& outputSignal)
{
if (!m_hasAnchor)
{
m_hasAnchor = true;
m_anchorTimeStamp = outputSignal.GetTimestamp();
}
// For the output we want to correlate it with the appropriate starting node
auto lastAggregateIter = m_lastAggregateItemMap.find(outputSignal);
ExecutionLogTreeItem* treeItem = nullptr;
if (lastAggregateIter != m_lastAggregateItemMap.end())
{
treeItem = lastAggregateIter->second;
if (treeItem->HasExecutionOutput()
|| treeItem->GetNodeId() != outputSignal.m_endpoint.GetNodeId())
{
treeItem = nullptr;
}
}
if (treeItem == nullptr)
{
treeItem = m_debugLogRoot->CreateExecutionItem(GetDataId(), outputSignal.m_nodeType, outputSignal, outputSignal.m_endpoint.GetNamedNodeId());
m_lastAggregateItemMap[outputSignal] = treeItem;
}
ScriptCanvas::Timestamp relativeTimeStamp = outputSignal.GetTimestamp() - m_anchorTimeStamp;
treeItem->RegisterExecutionOutput(outputSignal.m_endpoint.GetSlotId(), outputSignal.m_endpoint.GetSlotName(), AZStd::chrono::milliseconds(relativeTimeStamp));
for (auto dataMap : outputSignal.m_data)
{
AZStd::string valueString = dataMap.second.m_datum.ToString();
treeItem->RegisterDataOutput(dataMap.first, dataMap.first.m_name, valueString, GetTreeRoot()->GetUpdatePolicy() == DebugLogRootItem::UpdatePolicy::RealTime);
}
}
void LoggingDataAggregator::ProcessOutputDataSignal(const ScriptCanvas::OutputDataSignal& outputDataSignal)
{
if (!m_hasAnchor)
{
m_hasAnchor = true;
m_anchorTimeStamp = outputDataSignal.GetTimestamp();
}
// For the output we want to correlate it with the appropriate starting node
auto lastAggregateIter = m_lastAggregateItemMap.find(outputDataSignal);
ExecutionLogTreeItem* treeItem = nullptr;
if (lastAggregateIter != m_lastAggregateItemMap.end())
{
treeItem = lastAggregateIter->second;
if (treeItem->GetNodeId() != outputDataSignal.m_endpoint.GetNodeId())
{
treeItem = nullptr;
}
}
if (treeItem == nullptr)
{
treeItem = m_debugLogRoot->CreateExecutionItem(GetDataId(), outputDataSignal.m_nodeType, outputDataSignal, outputDataSignal.m_endpoint.GetNamedNodeId());
m_lastAggregateItemMap[outputDataSignal] = treeItem;
}
ScriptCanvas::Timestamp relativeTimeStamp = outputDataSignal.GetTimestamp() - m_anchorTimeStamp;
AZStd::string valueString = outputDataSignal.m_outputValue.m_datum.ToString();
treeItem->RegisterDataOutput(outputDataSignal.m_endpoint.GetSlotId(), outputDataSignal.m_endpoint.GetSlotName(), valueString, GetTreeRoot()->GetUpdatePolicy() == DebugLogRootItem::UpdatePolicy::RealTime);
}
void LoggingDataAggregator::ProcessAnnotateNode(const ScriptCanvas::AnnotateNodeSignal& annotateNodeSignal)
{
auto lastAggregateIter = m_lastAggregateItemMap.find(annotateNodeSignal);
if (lastAggregateIter != m_lastAggregateItemMap.end())
{
ExecutionLogTreeItem* treeItem = lastAggregateIter->second;
treeItem->RegisterAnnotation(annotateNodeSignal, GetTreeRoot()->GetUpdatePolicy() == DebugLogRootItem::UpdatePolicy::RealTime);
}
}
void LoggingDataAggregator::ProcessVariableChangedSignal([[maybe_unused]] const ScriptCanvas::VariableChange& variableChangeSignal)
{
}
DebugLogRootItem* LoggingDataAggregator::GetTreeRoot() const
{
return m_debugLogRoot;
}
void LoggingDataAggregator::RegisterEntityName(const AZ::EntityId& entityId, AZStd::string_view entityName)
{
auto nameIter = m_entityNameCache.find(entityId);
if (nameIter == m_entityNameCache.end())
{
m_entityNameCache[entityId] = entityName;
}
}
void LoggingDataAggregator::UnregisterEntityName(const AZ::EntityId& entityId)
{
// While we are capturing, we never want to update this list.
if (!IsCapturingData())
{
m_entityNameCache.erase(entityId);
}
}
void LoggingDataAggregator::OnRegistrationEnabled(const AZ::NamedEntityId&, const ScriptCanvas::GraphIdentifier&)
{
}
void LoggingDataAggregator::OnRegistrationDisabled(const AZ::NamedEntityId&, const ScriptCanvas::GraphIdentifier&)
{
}
void LoggingDataAggregator::ResetLog()
{
m_debugLogRoot->ResetData();
}
void LoggingDataAggregator::ResetData()
{
m_endpointData.clear();
m_variableData.clear();
for (const auto& registrationPair : m_registrationMap)
{
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEntityGraphUnregistered, registrationPair.first, registrationPair.second);
}
m_registrationMap.clear();
// Entity registrations are all transient. We need to clear them when we reset data.
// The assets should be static, so we can persist them.
m_loggingEntityMapping.clear();
m_lastAggregateItemMap.clear();
m_lastExecutionThreadMap.clear();
if (!IsCapturingData())
{
m_entityNameCache.clear();
}
m_hasAnchor = false;
m_anchorTimeStamp = ScriptCanvas::Timestamp(0);
}
void LoggingDataAggregator::RegisterScriptCanvas(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
bool foundMatch = false;
auto matchedRange = m_registrationMap.equal_range(entityId);
for (auto mapIter = matchedRange.first; mapIter != matchedRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
foundMatch = true;
AZ_Error("ScriptCanvas", false, "Received a duplicated registration callback.");
}
}
if (!foundMatch)
{
m_registrationMap.insert(AZStd::make_pair(entityId, graphIdentifier));
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEntityGraphRegistered, entityId, graphIdentifier);
}
}
void LoggingDataAggregator::UnregisterScriptCanvas(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
bool foundMatch = false;
{
auto matchedRange = m_registrationMap.equal_range(entityId);
for (auto mapIter = matchedRange.first; mapIter != matchedRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
foundMatch = true;
m_registrationMap.erase(mapIter);
break;
}
}
}
{
auto matchedRange = m_loggingEntityMapping.equal_range(entityId);
for (auto mapIter = matchedRange.first; mapIter != matchedRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
m_loggingEntityMapping.erase(mapIter);
break;
}
}
}
if (foundMatch)
{
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEntityGraphUnregistered, entityId, graphIdentifier);
}
}
}
@@ -0,0 +1,165 @@
/*
* 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 <AzCore/Component/EntityUtils.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <ScriptCanvas/Core/Endpoint.h>
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
#include <ScriptCanvas/Variable/VariableCore.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingTypes.h>
namespace ScriptCanvasEditor
{
class LoggingDataAggregator;
class LoggingDataRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
using BusIdType = LoggingDataId;
virtual bool IsCapturingData() const = 0;
// Return the object to allow for certain large data elements to be passed by reference instead of by value.
virtual const LoggingDataAggregator* FindLoggingData() const = 0;
virtual void EnableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) = 0;
virtual void DisableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) = 0;
virtual AZ::NamedEntityId FindNamedEntityId(const AZ::EntityId& entityId) = 0;
};
using LoggingDataRequestBus = AZ::EBus<LoggingDataRequests>;
class LoggingDataNotifications
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = LoggingDataId;
virtual void OnDataCaptureBegin() {};
virtual void OnDataCaptureEnd() {};
virtual void OnEntityGraphRegistered([[maybe_unused]] const AZ::NamedEntityId& entityId, [[maybe_unused]] const ScriptCanvas::GraphIdentifier& assetId) {};
virtual void OnEntityGraphUnregistered([[maybe_unused]] const AZ::NamedEntityId& entityId, [[maybe_unused]] const ScriptCanvas::GraphIdentifier& assetId) {}
virtual void OnEnabledStateChanged([[maybe_unused]] bool isEnabled, [[maybe_unused]] const AZ::NamedEntityId& namedEntityId, [[maybe_unused]] const ScriptCanvas::GraphIdentifier& graphIdentifier) {}
// TODO: Find a better spot for this
virtual void OnTreeItemAdded() {}
};
using LoggingDataNotificationBus = AZ::EBus<LoggingDataNotifications>;
// Container class for all of the local elements
class LoggingDataAggregator
: public LoggingDataRequestBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(LoggingDataAggregator, AZ::SystemAllocator,0);
LoggingDataAggregator();
~LoggingDataAggregator();
const LoggingDataId& GetDataId() const;
// LoggedDataRequests
const LoggingDataAggregator* FindLoggingData() const override;
void EnableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void DisableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
AZ::NamedEntityId FindNamedEntityId(const AZ::EntityId& entityId) override;
////
virtual bool IsCapturingData() const = 0;
virtual bool CanCaptureData() const = 0;
// Should be bus methods, but don't want to copy data
const EntityGraphRegistrationMap& GetEntityGraphRegistrationMap() const;
const LoggingEntityMap& GetLoggingEntityMap() const;
const LoggingAssetSet& GetLoggingAssetSet() const;
////
//
void ProcessSignal(const ScriptCanvas::Signal& signal);
void ProcessNodeStateChanged(const ScriptCanvas::NodeStateChange& stateChangeSignal);
void ProcessInputSignal(const ScriptCanvas::InputSignal& inputSignal);
void ProcessOutputSignal(const ScriptCanvas::OutputSignal& outputSignal);
void ProcessOutputDataSignal(const ScriptCanvas::OutputDataSignal& outputDataSignal);
void ProcessAnnotateNode(const ScriptCanvas::AnnotateNodeSignal& annotateNodeSignal);
void ProcessVariableChangedSignal(const ScriptCanvas::VariableChange& variableChangeSignal);
////
DebugLogRootItem* GetTreeRoot() const;
void RegisterEntityName(const AZ::EntityId& entityId, AZStd::string_view entityName);
void UnregisterEntityName(const AZ::EntityId& entityId);
protected:
// Methods here for child elements to do something with the data.
virtual void OnRegistrationEnabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
virtual void OnRegistrationDisabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void ResetData();
void ResetLog();
void RegisterScriptCanvas(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void UnregisterScriptCanvas(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
// Parsed Data Information
//
// Debug Context Information
//
// Will be used for visually displaying the data once we get to it.
AZStd::unordered_map< ScriptCanvas::Endpoint, AZStd::string > m_endpointData;
AZStd::unordered_map< ScriptCanvas::VariableId, AZStd::string > m_variableData;
////
AZStd::unordered_map< AZ::EntityId, AZStd::string > m_entityNameCache;
AZStd::unordered_map< ScriptCanvas::GraphInfo, ExecutionLogTreeItem* > m_lastAggregateItemMap;
AZStd::unordered_map< ScriptCanvas::GraphInfo, AZStd::vector<ExecutionIdentifier>> m_lastExecutionThreadMap;
private:
DebugLogRootItem* m_debugLogRoot;
// State Information
LoggingDataId m_id;
bool m_ignoreRegistrations;
bool m_hasAnchor;
ScriptCanvas::Timestamp m_anchorTimeStamp;
// TODO: Consider wrapping the three of these up into a single struct.
EntityGraphRegistrationMap m_registrationMap;
LoggingEntityMap m_loggingEntityMapping;
LoggingAssetSet m_loggedAssetSet;
};
}
@@ -0,0 +1,18 @@
/*
* 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 <Editor/View/Widgets/LoggingPanel/LoggingTypes.h>
namespace ScriptCanvasEditor
{
}
@@ -0,0 +1,34 @@
/*
* 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 <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/std/hash.h>
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
namespace ScriptCanvasEditor
{
struct ExecutionIdentifier
{
ExecutionIdentifier() = default;
};
constexpr AZ::ComponentId k_dynamicallySpawnedControllerId = static_cast<AZ::ComponentId>(-1);
typedef AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier> EntityGraphRegistrationMap;
typedef AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier> LoggingEntityMap;
typedef AZStd::unordered_set<ScriptCanvas::GraphIdentifier> LoggingAssetSet;
typedef AZ::EntityId LoggingDataId;
}
@@ -0,0 +1,96 @@
/*
* 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 <QMenu>
#include <QAction>
#include <AzQtComponents/Components/Widgets/SegmentBar.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindow.h>
#include <Editor/View/Widgets/LoggingPanel/ui_LoggingWindow.h>
namespace ScriptCanvasEditor
{
//////////////////
// LoggingWindow
//////////////////
LoggingWindow::LoggingWindow(QWidget* parentWidget)
: AzQtComponents::StyledDockWidget(parentWidget)
, m_ui(new Ui::LoggingWindow)
{
m_ui->setupUi(this);
// Hack to hide the close button on the first tab. Since we always want it open.
m_ui->tabWidget->setTabsClosable(true);
m_ui->tabWidget->tabBar()->setTabButton(0, QTabBar::ButtonPosition::RightSide, nullptr);
m_ui->tabWidget->tabBar()->setTabButton(0, QTabBar::ButtonPosition::LeftSide, nullptr);
m_ui->segmentWidget->addTab(new QWidget(m_ui->segmentWidget), QStringLiteral("Entities"));
m_ui->segmentWidget->addTab(new QWidget(m_ui->segmentWidget), QStringLiteral("Graphs"));
connect(m_ui->segmentWidget, &AzQtComponents::SegmentControl::currentChanged, [this](int newIndex) {
m_ui->stackedWidget->setCurrentIndex(newIndex);
});
QObject::connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, &LoggingWindow::OnActiveTabChanged);
AzQtComponents::TabWidget::applySecondaryStyle(m_ui->tabWidget, false);
m_entityPageIndex = m_ui->stackedWidget->indexOf(m_ui->entitiesPage);
m_graphPageIndex = m_ui->stackedWidget->indexOf(m_ui->graphsPage);
OnActiveTabChanged(m_ui->tabWidget->currentIndex());
PivotOnEntities();
}
LoggingWindow::~LoggingWindow()
{
}
void LoggingWindow::OnActiveTabChanged([[maybe_unused]] int index)
{
LoggingWindowSession* windowSession = qobject_cast<LoggingWindowSession*>(m_ui->tabWidget->currentWidget());
if (windowSession)
{
m_activeDataId = windowSession->GetDataId();
}
m_ui->entityPivotWidget->SwitchDataSource(m_activeDataId);
m_ui->graphPivotWidget->SwitchDataSource(m_activeDataId);
}
void LoggingWindow::PivotOnEntities()
{
m_ui->stackedWidget->setCurrentIndex(m_ui->stackedWidget->indexOf(m_ui->entitiesPage));
}
void LoggingWindow::PivotOnGraphs()
{
m_ui->stackedWidget->setCurrentIndex(m_ui->stackedWidget->indexOf(m_ui->graphsPage));
}
PivotTreeWidget* LoggingWindow::GetActivePivotWidget() const
{
if (m_ui->stackedWidget->currentIndex() == m_entityPageIndex)
{
return m_ui->entityPivotWidget;
}
return nullptr;
}
#include <Editor/View/Widgets/LoggingPanel/moc_LoggingWindow.cpp>
}
@@ -0,0 +1,66 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QButtonGroup>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#endif
namespace Ui
{
class LoggingWindow;
}
namespace ScriptCanvasEditor
{
class PivotTreeWidget;
class LoggingWindow
: public AzQtComponents::StyledDockWidget
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(LoggingWindow, AZ::SystemAllocator, 0);
LoggingWindow(QWidget* parentWidget = nullptr);
virtual ~LoggingWindow();
protected:
void OnActiveTabChanged(int index);
void PivotOnEntities();
void PivotOnGraphs();
private:
PivotTreeWidget* GetActivePivotWidget() const;
AZStd::unique_ptr<Ui::LoggingWindow> m_ui;
QButtonGroup m_pivotGroup;
LoggingDataId m_activeDataId;
int m_entityPageIndex;
int m_graphPageIndex;
};
}
@@ -0,0 +1,354 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoggingWindow</class>
<widget class="QDockWidget" name="LoggingWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>603</width>
<height>316</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="windowTitle">
<string>Debugger</string>
</property>
<widget class="QWidget" name="center">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>603</width>
<height>294</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>35</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::SegmentControl" name="segmentWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="topMargin" stdset="0">
<number>0</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>35</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="entitiesPage">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="ScriptCanvasEditor::EntityPivotTreeWidget" name="entityPivotWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="graphsPage">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="ScriptCanvasEditor::GraphPivotTreeWidget" name="graphPivotWidget" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>4</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzQtComponents::TabWidget" name="tabWidget">
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="tabsClosable">
<bool>true</bool>
</property>
<property name="movable">
<bool>false</bool>
</property>
<widget class="ScriptCanvasEditor::LiveLoggingWindowSession" name="emptyCapture">
<attribute name="title">
<string>Live</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ScriptCanvasEditor::EntityPivotTreeWidget</class>
<extends>QWidget</extends>
<header location="global">Editor/View/Widgets/LoggingPanel/PivotTree/EntityPivotTree/EntityPivotTree.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ScriptCanvasEditor::LiveLoggingWindowSession</class>
<extends>QWidget</extends>
<header location="global">Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingWindowSession.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ScriptCanvasEditor::GraphPivotTreeWidget</class>
<extends>QWidget</extends>
<header location="global">Editor/View/Widgets/LoggingPanel/PivotTree/GraphPivotTree/GraphPivotTree.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::TabWidget</class>
<extends>QTabWidget</extends>
<header location="global">AzQtComponents/Components/Widgets/TabWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::SegmentControl</class>
<extends>QFrame</extends>
<header location="global">AzQtComponents/Components/Widgets/SegmentControl.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,483 @@
/*
* 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 <QScrollBar>
#include <QGraphicsItem>
#include <QScopedValueRollback>
#include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/Widgets/StyledItemDelegates/IconDecoratedNameDelegate.h>
#include <GraphCanvas/Utils/GraphUtils.h>
#include <Editor/View/Widgets/AssetGraphSceneDataBus.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowSession.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/GraphCanvas/MappingBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
namespace ScriptCanvasEditor
{
/////////////////////////////
// LoggingWindowFilterModel
/////////////////////////////
bool LoggingWindowFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
if (m_logFilter.IsEmpty())
{
return true;
}
QAbstractItemModel* model = sourceModel();
QModelIndex index = model->index(sourceRow, 0, sourceParent);
DebugLogTreeItem* treeItem = static_cast<DebugLogTreeItem*>(index.internalPointer());
if (treeItem)
{
return treeItem->MatchesFilter(m_logFilter);
}
return false;
}
void LoggingWindowFilterModel::SetFilter(const QString& filter)
{
m_filter = filter;
m_logFilter.m_filter = QRegExp(m_filter, Qt::CaseInsensitive);
invalidateFilter();
}
void LoggingWindowFilterModel::ClearFilter()
{
SetFilter("");
}
bool LoggingWindowFilterModel::HasFilter() const
{
return !m_filter.isEmpty();
}
/////////////////////////
// LoggingWindowSession
/////////////////////////
LoggingWindowSession::LoggingWindowSession(QWidget* parentWidget)
: QWidget(parentWidget)
, m_ui(new Ui::LoggingWindowSession())
, m_clearSelectionOnSceneSelectionChange(true)
, m_scrollToBottom(true)
, m_debugRoot(nullptr)
, m_treeModel(nullptr)
, m_filterModel(nullptr)
{
m_ui->setupUi(this);
QObject::connect(m_ui->captureButton, &QToolButton::clicked, this, &LoggingWindowSession::OnCaptureButtonPressed);
QObject::connect(m_ui->expandAll, &QToolButton::clicked, this, &LoggingWindowSession::OnExpandAll);
QObject::connect(m_ui->collapseAll, &QToolButton::clicked, this, &LoggingWindowSession::OnCollapseAll);
QObject::connect(m_ui->targetSelector, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &LoggingWindowSession::OnTargetChanged);
QObject::connect(m_ui->logTree->verticalScrollBar(), &QScrollBar::valueChanged, this, &LoggingWindowSession::OnLogScrolled);
QObject::connect(m_ui->logTree, &QTreeView::expanded, this, &LoggingWindowSession::OnLogItemExpanded);
QObject::connect(m_ui->logTree->verticalScrollBar(), &QScrollBar::rangeChanged, this, &LoggingWindowSession::OnLogRangeChanged);
QObject::connect(m_ui->filterWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, this, &LoggingWindowSession::OnSearchFilterChanged);
m_ui->filterWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
m_ui->logTree->setMouseTracking(true);
QObject::connect(m_ui->logTree, &QTreeView::clicked, this, &LoggingWindowSession::OnLogClicked);
QObject::connect(m_ui->logTree, &QTreeView::doubleClicked, this, &LoggingWindowSession::OnLogDoubleClicked);
m_focusDelayTimer.setInterval(125);
m_focusDelayTimer.setSingleShot(true);
QObject::connect(&m_focusDelayTimer, &QTimer::timeout, this, &LoggingWindowSession::HandleQueuedFocus);
GraphCanvas::AssetEditorNotificationBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
AZ::EntityId graphCanvasId;
GeneralRequestBus::BroadcastResult(graphCanvasId, &GeneralRequests::GetActiveGraphCanvasGraphId);
OnActiveGraphChanged(graphCanvasId);
}
LoggingWindowSession::~LoggingWindowSession()
{
}
const LoggingDataId& LoggingWindowSession::GetDataId() const
{
return m_loggingDataId;
}
void LoggingWindowSession::ClearFilter()
{
m_ui->filterWidget->ClearTextFilter();
}
void LoggingWindowSession::OnActiveGraphChanged(const AZ::EntityId& graphId)
{
ClearLoggingSelection();
if (GraphCanvas::SceneNotificationBus::Handler::BusIsConnected())
{
GraphCanvas::SceneNotificationBus::Handler::BusDisconnect();
}
if (graphId.IsValid())
{
GraphCanvas::SceneNotificationBus::Handler::BusConnect(graphId);
}
}
void LoggingWindowSession::OnSelectionChanged()
{
ClearLoggingSelection();
}
void LoggingWindowSession::RegisterTreeRoot(DebugLogRootItem* debugRoot)
{
m_debugRoot = debugRoot;
m_treeModel = aznew GraphCanvas::GraphCanvasTreeModel(debugRoot, this);
m_filterModel = aznew LoggingWindowFilterModel();
m_filterModel->setSourceModel(m_treeModel);
m_ui->logTree->setModel(m_filterModel);
m_ui->logTree->header()->setStretchLastSection(false);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::NodeName, QHeaderView::ResizeMode::Stretch);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::Input, QHeaderView::ResizeMode::Stretch);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::Output, QHeaderView::ResizeMode::Stretch);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::TimeStep, QHeaderView::ResizeMode::Fixed);
m_ui->logTree->header()->resizeSection(DebugLogTreeItem::Column::TimeStep, 75);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::ScriptName, QHeaderView::ResizeMode::Fixed);
m_ui->logTree->header()->resizeSection(DebugLogTreeItem::Column::ScriptName, 150);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::SourceEntity, QHeaderView::ResizeMode::Fixed);
m_ui->logTree->header()->resizeSection(DebugLogTreeItem::Column::SourceEntity, 200);
m_ui->logTree->setItemDelegateForColumn(DebugLogTreeItem::Column::NodeName, aznew GraphCanvas::IconDecoratedNameDelegate(this));
QObject::connect(m_ui->logTree->selectionModel(), &QItemSelectionModel::selectionChanged, this, &LoggingWindowSession::OnLogSelectionChanged);
}
void LoggingWindowSession::SetDataId(const LoggingDataId& loggingDataId)
{
if (!m_loggingDataId.IsValid())
{
m_loggingDataId = loggingDataId;
}
}
void LoggingWindowSession::OnExpandAll()
{
m_ui->logTree->expandAll();
ScrollToSelection();
}
void LoggingWindowSession::OnCollapseAll()
{
m_ui->logTree->collapseAll();
ScrollToSelection();
}
void LoggingWindowSession::OnSearchFilterChanged(const QString& filterString)
{
m_filterModel->SetFilter(filterString);
}
void LoggingWindowSession::OnLogScrolled(int value)
{
if (m_ui->logTree->verticalScrollBar()->isEnabled())
{
if (m_ui->logTree->verticalScrollBar()->maximum() == value)
{
m_scrollToBottom = true;
}
else
{
m_scrollToBottom = false;
}
}
else
{
m_scrollToBottom = true;
}
}
void LoggingWindowSession::OnLogItemExpanded([[maybe_unused]] const QModelIndex& modelIndex)
{
m_scrollToBottom = false;
}
void LoggingWindowSession::OnLogRangeChanged([[maybe_unused]] int min, int max)
{
if (m_scrollToBottom)
{
m_ui->logTree->verticalScrollBar()->setValue(max);
}
if (!m_ui->logTree->verticalScrollBar()->isEnabled())
{
m_scrollToBottom = true;
}
}
void LoggingWindowSession::OnLogClicked(const QModelIndex& modelIndex)
{
if (modelIndex.column() == DebugLogTreeItem::Column::ScriptName)
{
QModelIndex sourceIndex = m_filterModel->mapToSource(modelIndex);
DebugLogTreeItem* treeItem = static_cast<DebugLogTreeItem*>(sourceIndex.internalPointer());
if (ExecutionLogTreeItem* executionItem = azrtti_cast<ExecutionLogTreeItem*>(treeItem))
{
QScopedValueRollback<bool> valueRollback(m_clearSelectionOnSceneSelectionChange, false);
const AZ::Data::AssetId& assetId = executionItem->GetAssetId();
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAssetId, assetId);
}
}
}
void LoggingWindowSession::OnLogDoubleClicked(const QModelIndex& modelIndex)
{
ExecutionLogTreeItem* executionItem = ResolveExecutionItem(modelIndex);
if (executionItem)
{
const AZ::Data::AssetId& assetId = executionItem->GetAssetId();
bool isAssetOpen = false;
GeneralRequestBus::BroadcastResult(isAssetOpen, &GeneralRequests::IsScriptCanvasAssetOpen, assetId);
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAssetId, assetId);
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, assetId);
if (isAssetOpen)
{
FocusOnElement(assetId, executionItem->GetScriptCanvasAssetNodeId());
}
else
{
m_assetId = assetId;
m_assetNodeId = executionItem->GetScriptCanvasAssetNodeId();
m_focusDelayTimer.stop();
m_focusDelayTimer.start();
}
}
}
void LoggingWindowSession::OnLogSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
for (const QModelIndex& deselectedIndex : deselected.indexes())
{
if (deselectedIndex.column() != 0)
{
continue;
}
ExecutionLogTreeItem* executionItem = ResolveExecutionItem(deselectedIndex);
if (executionItem)
{
RemoveHighlight(executionItem->GetAssetId(), executionItem->GetScriptCanvasAssetNodeId());
}
}
for (const QModelIndex& selectedIndex : selected.indexes())
{
if (selectedIndex.column() != 0)
{
continue;
}
ExecutionLogTreeItem* executionItem = ResolveExecutionItem(selectedIndex);
if (executionItem)
{
HighlightElement(executionItem->GetAssetId(), executionItem->GetScriptCanvasAssetNodeId());
}
}
}
ExecutionLogTreeItem* LoggingWindowSession::ResolveExecutionItem(const QModelIndex& proxyModelIndex)
{
QModelIndex sourceIndex = m_filterModel->mapToSource(proxyModelIndex);
DebugLogTreeItem* treeItem = static_cast<DebugLogTreeItem*>(sourceIndex.internalPointer());
DebugLogTreeItem* parentItem = static_cast<DebugLogTreeItem*>(treeItem->GetParent());
ExecutionLogTreeItem* executionItem = azrtti_cast<ExecutionLogTreeItem*>(treeItem);
while (executionItem == nullptr && parentItem != nullptr)
{
executionItem = azrtti_cast<ExecutionLogTreeItem*>(parentItem);
parentItem = static_cast<DebugLogTreeItem*>(parentItem->GetParent());
}
return executionItem;
}
void LoggingWindowSession::HandleQueuedFocus()
{
AZ::EntityId activeGraphCanvasGraphId;
GeneralRequestBus::BroadcastResult(activeGraphCanvasGraphId, &GeneralRequests::GetActiveGraphCanvasGraphId);
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, m_assetId);
if (activeGraphCanvasGraphId == graphCanvasGraphId)
{
FocusOnElement(m_assetId, m_assetNodeId);
m_focusDelayTimer.stop();
m_assetId.SetInvalid();
m_assetNodeId.SetInvalid();
}
}
void LoggingWindowSession::FocusOnElement(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId)
{
GraphCanvas::FocusConfig focusConfig;
AZ::EntityId scriptCanvasNodeId;
AssetGraphSceneBus::BroadcastResult(scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, assetId, assetNodeId);
GraphCanvas::NodeId graphCanvasNodeId;
SceneMemberMappingRequestBus::EventResult(graphCanvasNodeId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
if (GraphCanvas::GraphUtils::IsNodeGroup(graphCanvasNodeId))
{
focusConfig.m_spacingType = GraphCanvas::FocusConfig::SpacingType::GridStep;
focusConfig.m_spacingAmount = 1;
}
else
{
focusConfig.m_spacingType = GraphCanvas::FocusConfig::SpacingType::Scalar;
focusConfig.m_spacingAmount = 2;
}
AZStd::vector< AZ::EntityId > memberIds = { graphCanvasNodeId };
GraphCanvas::GraphUtils::FocusOnElements(memberIds, focusConfig);
{
QScopedValueRollback<bool> maintainSelection(m_clearSelectionOnSceneSelectionChange, false);
RemoveHighlight(assetId, assetNodeId);
GraphCanvas::GraphId graphId;
GraphCanvas::SceneMemberRequestBus::EventResult(graphId, graphCanvasNodeId, &GraphCanvas::SceneMemberRequests::GetScene);
GraphCanvas::SceneRequestBus::Event(graphId, &GraphCanvas::SceneRequests::ClearSelection);
GraphCanvas::SceneMemberUIRequestBus::Event(graphCanvasNodeId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
}
}
void LoggingWindowSession::HighlightElement(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId)
{
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, assetId);
if (graphCanvasGraphId.IsValid())
{
AZ::EntityId scriptCanvasNodeId;
AssetGraphSceneBus::BroadcastResult(scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, assetId, assetNodeId);
GraphCanvas::NodeId graphCanvasNodeId;
SceneMemberMappingRequestBus::EventResult(graphCanvasNodeId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
GraphCanvas::SceneMemberGlowOutlineConfiguration glowConfiguration;
glowConfiguration.m_sceneMember = graphCanvasNodeId;
glowConfiguration.m_blurRadius = 5;
glowConfiguration.m_pen = QPen();
glowConfiguration.m_pen.setBrush(QColor(243, 129, 29));
glowConfiguration.m_pen.setWidth(5);
glowConfiguration.m_pulseRate = AZStd::chrono::milliseconds(2500);
glowConfiguration.m_zValue = 0;
GraphCanvas::GraphicsEffectId effectId;
GraphCanvas::SceneRequestBus::EventResult(effectId, graphCanvasGraphId, &GraphCanvas::SceneRequests::CreateGlowOnSceneMember, glowConfiguration);
auto effectIter = m_highlightEffects.find(assetNodeId);
if (effectIter != m_highlightEffects.end())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::CancelGraphicsEffect, effectIter->second);
}
m_highlightEffects[assetNodeId] = effectId;
}
}
void LoggingWindowSession::RemoveHighlight(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId)
{
auto effectIter = m_highlightEffects.find(assetNodeId);
if (effectIter != m_highlightEffects.end())
{
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, assetId);
if (graphCanvasGraphId.IsValid())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::CancelGraphicsEffect, effectIter->second);
}
m_highlightEffects.erase(effectIter);
}
}
void LoggingWindowSession::ScrollToSelection()
{
for (auto selectedIndex : m_ui->logTree->selectionModel()->selectedIndexes())
{
m_ui->logTree->scrollTo(selectedIndex);
}
}
void LoggingWindowSession::ClearLoggingSelection()
{
if (m_clearSelectionOnSceneSelectionChange)
{
m_ui->logTree->clearSelection();
}
}
#include <Editor/View/Widgets/LoggingPanel/moc_LoggingWindowSession.cpp>
}
@@ -0,0 +1,148 @@
/*
* 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
// qbrush.h(118): warning C4251: 'QBrush::d': class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
// qwidget.h(858): warning C4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#if !defined(Q_MOC_RUN)
#include <QAbstractItemModel>
#include <QIcon>
#include <QSortFilterProxyModel>
#include <QWidget>
AZ_POP_DISABLE_WARNING
#include <AzCore/Component/NamedEntityId.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Editor/AssetEditorBus.h>
#include <GraphCanvas/Styling/StyleHelper.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeModel.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h>
#include <ScriptCanvas/Core/Endpoint.h>
#include <ScriptCanvas/Core/NodeBus.h>
// Qt Generated
// warning C4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <Editor/View/Widgets/LoggingPanel/ui_LoggingWindowSession.h>
#endif
AZ_POP_DISABLE_WARNING
namespace ScriptCanvasEditor
{
class LoggingWindowFilterModel
: public QSortFilterProxyModel
{
public:
AZ_CLASS_ALLOCATOR(LoggingWindowFilterModel, AZ::SystemAllocator, 0);
LoggingWindowFilterModel() = default;
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
void SetFilter(const QString& filter);
void ClearFilter();
bool HasFilter() const;
private:
QString m_filter;
DebugLogFilter m_logFilter;
};
class LoggingWindowSession
: public QWidget
, public GraphCanvas::AssetEditorNotificationBus::Handler
, public GraphCanvas::SceneNotificationBus::Handler
{
Q_OBJECT
protected:
LoggingWindowSession(QWidget* parentWidget = nullptr);
public:
~LoggingWindowSession() override;
const LoggingDataId& GetDataId() const;
void ClearFilter();
// GraphCanvas::AssetEditorNotificationBus
void OnActiveGraphChanged(const AZ::EntityId& graphId) override;
////
// GraphCanvas::SceneNotificationBus
void OnSelectionChanged() override;
////
protected:
void RegisterTreeRoot(DebugLogRootItem* debugRoot);
void SetDataId(const LoggingDataId& loggingDataId);
virtual void OnCaptureButtonPressed() = 0;
virtual void OnPlaybackButtonPressed() = 0;
virtual void OnOptionsButtonPressed() = 0;
virtual void OnTargetChanged(int currentIndex) = 0;
void OnExpandAll();
void OnCollapseAll();
protected:
AZStd::unique_ptr< Ui::LoggingWindowSession > m_ui;
private:
void OnSearchFilterChanged(const QString& filterString);
void OnLogScrolled(int value);
void OnLogItemExpanded(const QModelIndex& modelIndex);
void OnLogRangeChanged(int min, int max);
void OnLogClicked(const QModelIndex& modelIndex);
void OnLogDoubleClicked(const QModelIndex& modelIndex);
void OnLogSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
ExecutionLogTreeItem* ResolveExecutionItem(const QModelIndex& proxyModelIndex);
void HandleQueuedFocus();
void FocusOnElement(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId);
void HighlightElement(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId);
void RemoveHighlight(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId);
void ScrollToSelection();
void ClearLoggingSelection();
bool m_clearSelectionOnSceneSelectionChange;
bool m_scrollToBottom;
LoggingDataId m_loggingDataId;
DebugLogRootItem* m_debugRoot;
GraphCanvas::GraphCanvasTreeModel* m_treeModel;
LoggingWindowFilterModel* m_filterModel;
AZStd::unordered_map< AZ::EntityId, GraphCanvas::GraphicsEffectId > m_highlightEffects;
QTimer m_focusDelayTimer;
AZ::Data::AssetId m_assetId;
AZ::EntityId m_assetNodeId;
};
}
@@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoggingWindowSession</class>
<widget class="QWidget" name="LoggingWindowSession">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>897</width>
<height>108</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="targetSelector">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="captureButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/capture_offline.png</normaloff>:/ScriptCanvasEditorResources/Resources/capture_offline.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="autoCaptureToggle">
<property name="toolTip">
<string>Controls whether or not capture will enable as soon as the desired target connects.</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/auto_record.png</normaloff>:/ScriptCanvasEditorResources/Resources/auto_record.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="liveUpdatesToggle">
<property name="toolTip">
<string>Controls whether or not the Logging View live updates with the Captured Data (Usually want to disable for performance reasons)</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/live_update.png</normaloff>:/ScriptCanvasEditorResources/Resources/live_update.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="expandAll">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/ExpandAll_Icon.png</normaloff>:/ScriptCanvasEditorResources/Resources/ExpandAll_Icon.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="collapseAll">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/CollapseAll_Icon.png</normaloff>:/ScriptCanvasEditorResources/Resources/CollapseAll_Icon.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="filterWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="openIcon">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTreeView" name="logTree">
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QWidget</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../Windows/ScriptCanvasEditorResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,947 @@
/*
* 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 <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Utils/GraphUtils.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/Include/ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#include <Editor/View/Widgets/AssetGraphSceneDataBus.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/GraphCanvas/MappingBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
namespace ScriptCanvasEditor
{
/////////////////////
// DebugLogTreeItem
/////////////////////
bool DebugLogTreeItem::MatchesFilter(const DebugLogFilter& treeFilter)
{
DebugLogTreeItem* parent = static_cast<DebugLogTreeItem*>(GetParent());
while (parent)
{
// We don't want to match against the root, since it always matches.
// So only check things that have a valid parent.
DebugLogTreeItem* nextParent = static_cast<DebugLogTreeItem*>(parent->GetParent());
if (nextParent != nullptr && parent->OnMatchesFilter(treeFilter))
{
return true;
}
parent = static_cast<DebugLogTreeItem*>(parent->GetParent());
}
DebugLogTreeItem* currentItem = this;
AZStd::unordered_set< DebugLogTreeItem* > children;
while (currentItem)
{
if (currentItem->OnMatchesFilter(treeFilter))
{
return true;
}
for (int i = 0; i < currentItem->GetChildCount(); ++i)
{
children.insert(static_cast<DebugLogTreeItem*>(currentItem->FindChildByRow(i)));
}
if (!children.empty())
{
currentItem = (*children.begin());
children.erase(children.begin());
}
else
{
currentItem = nullptr;
}
}
return false;
}
const ScriptCanvas::Endpoint& DebugLogTreeItem::GetIncitingEndpoint() const
{
return m_incitingEndpoint;
}
bool DebugLogTreeItem::IsTriggeredBy(const ScriptCanvas::Endpoint& endpoint) const
{
return m_incitingEndpoint == endpoint;
}
Qt::ItemFlags DebugLogTreeItem::Flags([[maybe_unused]] const QModelIndex& index) const
{
return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable;
}
int DebugLogTreeItem::GetColumnCount() const
{
return Column::Count;
}
void DebugLogTreeItem::SetIncitingEndpoint(const ScriptCanvas::Endpoint& endpoint)
{
m_incitingEndpoint = endpoint;
}
/////////////////////
// DebugLogRootItem
/////////////////////
DebugLogRootItem::DebugLogRootItem()
: m_updatePolicy(UpdatePolicy::Batched)
{
m_additionTimer.setSingleShot(true);
m_additionTimer.setInterval(1000);
QObject::connect(&m_additionTimer, &QTimer::timeout, [this]() { this->RedoLayout(); });
}
DebugLogRootItem::~DebugLogRootItem()
{
}
ExecutionLogTreeItem* DebugLogRootItem::CreateExecutionItem(const LoggingDataId& loggingDataId, const ScriptCanvas::NodeTypeIdentifier& nodeType, const ScriptCanvas::GraphInfo& graphInfo, const ScriptCanvas::NamedNodeId& nodeId)
{
ExecutionLogTreeItem* treeItem = nullptr;
bool signalChanged = false;
if (m_updatePolicy == UpdatePolicy::Batched)
{
if (!m_additionTimer.isActive())
{
m_additionTimer.start();
}
}
if (m_updatePolicy == UpdatePolicy::SingleTime)
{
treeItem = CreateChildNodeWithoutAddSignal<ExecutionLogTreeItem>(loggingDataId, nodeType, graphInfo, nodeId);
}
else
{
treeItem = CreateChildNode<ExecutionLogTreeItem>(loggingDataId, nodeType, graphInfo, nodeId);
}
return treeItem;
}
QVariant DebugLogRootItem::Data([[maybe_unused]] const QModelIndex& index, [[maybe_unused]] int role) const
{
return QVariant();
}
void DebugLogRootItem::ResetData()
{
SignalLayoutAboutToBeChanged();
ClearChildren();
SignalLayoutChanged();
}
void DebugLogRootItem::SetUpdatePolicy(UpdatePolicy updatePolicy)
{
if (m_updatePolicy != updatePolicy)
{
m_updatePolicy = updatePolicy;
m_additionTimer.stop();
}
}
DebugLogRootItem::UpdatePolicy DebugLogRootItem::GetUpdatePolicy() const
{
return m_updatePolicy;
}
void DebugLogRootItem::RedoLayout()
{
m_additionTimer.stop();
SignalLayoutAboutToBeChanged();
SignalLayoutChanged();
}
/////////////////////////
// ExecutionLogTreeItem
/////////////////////////
ExecutionLogTreeItem::ExecutionLogTreeItem(const LoggingDataId& loggingDataId, const ScriptCanvas::NodeTypeIdentifier& nodeType, const ScriptCanvas::GraphInfo& graphInfo, const ScriptCanvas::NamedNodeId& nodeId)
: m_loggingDataId(loggingDataId)
, m_nodeType(nodeType)
, m_graphInfo(graphInfo)
, m_scriptCanvasAssetNodeId(nodeId)
, m_iconPixmap(nullptr)
{
m_paletteConfiguration.m_iconPalette = "NodePaletteTypeIcon";
m_paletteConfiguration.SetColorPalette("MethodNodeTitlePalette");
AZ::NamedEntityId entityName;
LoggingDataRequestBus::EventResult(entityName, m_loggingDataId, &LoggingDataRequests::FindNamedEntityId, m_graphInfo.m_runtimeEntity);
m_sourceEntityName = entityName.ToString().c_str();
m_displayName = nodeId.m_name.c_str();
ScrapeBehaviorContextData();
ScrapeGraphCanvasData();
m_inputName = "---";
m_outputName = "---";
GeneralAssetNotificationBus::Handler::BusConnect(GetAssetId());
}
QVariant ExecutionLogTreeItem::Data(const QModelIndex& index, int role) const
{
switch (index.column())
{
case Column::NodeName:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_displayName;
}
else if (role == Qt::DecorationRole)
{
if (m_iconPixmap != nullptr)
{
return (*m_iconPixmap);
}
}
}
break;
case Column::Input:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_inputName;
}
}
break;
case Column::Output:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_outputName;
}
}
break;
case Column::TimeStep:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_timeString;
}
}
break;
case Column::ScriptName:
{
if (role == Qt::DisplayRole)
{
return m_graphName;
}
else if (role == Qt::ToolTipRole)
{
return m_relativeGraphPath;
}
else if (role == Qt::ForegroundRole)
{
return QColor(42,132,252);
}
else if (role == Qt::FontRole)
{
QFont font;
font.setUnderline(true);
return font;
}
}
break;
case Column::SourceEntity:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_sourceEntityName;
}
}
break;
default:
break;
}
return QVariant();
}
AZ::EntityId ExecutionLogTreeItem::GetNodeId() const
{
return m_scriptCanvasAssetNodeId;
}
void ExecutionLogTreeItem::RegisterAnnotation(const ScriptCanvas::AnnotateNodeSignal& annotationSignal, bool allowAddSignal)
{
// The QTreeView does have a setFirstColumnSpanned, but it doesn't seem dynamic, nor model driven.
// So I don't want to use it.
if (allowAddSignal)
{
CreateChildNode<NodeAnnotationTreeItem>(annotationSignal.m_annotationLevel, annotationSignal.m_annotation);
}
else
{
CreateChildNodeWithoutAddSignal<NodeAnnotationTreeItem>(annotationSignal.m_annotationLevel, annotationSignal.m_annotation);
}
}
void ExecutionLogTreeItem::RegisterDataInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::string_view dataString, bool allowAddSignal)
{
if (!HasExecutionInput() && !HasExecutionOutput())
{
ResolveWrapperNode();
}
DataLogTreeItem* dataTreeItem = nullptr;
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* testLogItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (testLogItem && !testLogItem->HasInput())
{
dataTreeItem = testLogItem;
break;
}
}
if (dataTreeItem == nullptr)
{
if (allowAddSignal)
{
dataTreeItem = CreateChildNode<DataLogTreeItem>(GetGraphIdentifier());
}
else
{
dataTreeItem = CreateChildNodeWithoutAddSignal<DataLogTreeItem>(GetGraphIdentifier());
}
}
ScriptCanvas::Endpoint endpoint(m_scriptCanvasAssetNodeId, slotId);
dataTreeItem->RegisterDataInput(incitingEndpoint, endpoint, slotName, dataString);
}
void ExecutionLogTreeItem::RegisterDataOutput(const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::string_view dataString, bool allowAddSignal)
{
if (!HasExecutionInput() && !HasExecutionOutput())
{
ResolveWrapperNode();
}
DataLogTreeItem* dataTreeItem = nullptr;
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* testLogItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (testLogItem && !testLogItem->HasOutput())
{
dataTreeItem = testLogItem;
break;
}
}
if (dataTreeItem == nullptr)
{
if (allowAddSignal)
{
dataTreeItem = CreateChildNode<DataLogTreeItem>(GetGraphIdentifier());
}
else
{
dataTreeItem = CreateChildNodeWithoutAddSignal<DataLogTreeItem>(GetGraphIdentifier());
}
}
ScriptCanvas::Endpoint endpoint(m_scriptCanvasAssetNodeId, slotId);
dataTreeItem->RegisterDataOutput(endpoint, slotName, dataString);
}
void ExecutionLogTreeItem::RegisterExecutionInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::chrono::milliseconds relativeExecution)
{
m_timeString = QTime::fromMSecsSinceStartOfDay(aznumeric_cast<int>(relativeExecution.count())).toString("mm:ss.zzz");
m_inputSlot = slotId;
m_inputName = slotName.data();
SetIncitingEndpoint(incitingEndpoint);
if (!HasExecutionOutput())
{
ResolveWrapperNode();
}
PopulateInputSlotData();
SignalDataChanged();
}
bool ExecutionLogTreeItem::HasExecutionInput() const
{
return m_inputSlot.IsValid();
}
void ExecutionLogTreeItem::RegisterExecutionOutput(const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::chrono::milliseconds relativeExecution)
{
if (!HasExecutionInput())
{
m_timeString = QTime::fromMSecsSinceStartOfDay(aznumeric_cast<int>(relativeExecution.count())).toString("mm:ss.zzz");
}
m_outputSlot = slotId;
m_outputName = slotName.data();
if (!HasExecutionInput())
{
ResolveWrapperNode();
}
PopulateOutputSlotData();
SignalDataChanged();
}
bool ExecutionLogTreeItem::HasExecutionOutput() const
{
return m_outputSlot.IsValid();
}
void ExecutionLogTreeItem::OnStylesUnloaded()
{
m_iconPixmap = nullptr;
}
void ExecutionLogTreeItem::OnStylesLoaded()
{
GraphCanvas::StyleManagerRequestBus::EventResult(m_iconPixmap, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetConfiguredPaletteIcon, m_paletteConfiguration);
SignalDataChanged();
}
void ExecutionLogTreeItem::OnAssetVisualized()
{
ScrapeGraphCanvasData();
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* dataLogTreeItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (dataLogTreeItem)
{
dataLogTreeItem->ScrapeData();
}
}
}
void ExecutionLogTreeItem::OnAssetUnloaded()
{
EditorGraphNotificationBus::Handler::BusDisconnect();
m_scriptCanvasNodeId.SetInvalid();
m_graphCanvasGraphId.SetInvalid();
m_graphCanvasNodeId.SetInvalid();
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* dataLogTreeItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (dataLogTreeItem)
{
dataLogTreeItem->InvalidateEditorIds();
}
}
}
void ExecutionLogTreeItem::OnGraphCanvasSceneDisplayed()
{
m_graphCanvasGraphId.SetInvalid();
m_graphCanvasNodeId.SetInvalid();
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* dataLogTreeItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (dataLogTreeItem)
{
dataLogTreeItem->InvalidateGraphCanvasIds();
}
}
ScrapeGraphCanvasData();
}
const ScriptCanvas::GraphIdentifier& ExecutionLogTreeItem::GetGraphIdentifier() const
{
return m_graphInfo.m_graphIdentifier;
}
const AZ::Data::AssetId& ExecutionLogTreeItem::GetAssetId() const
{
return m_graphInfo.m_graphIdentifier.m_assetId;
}
AZ::EntityId ExecutionLogTreeItem::GetScriptCanvasAssetNodeId() const
{
return m_scriptCanvasAssetNodeId;
}
GraphCanvas::NodeId ExecutionLogTreeItem::GetGraphCanvasNodeId() const
{
return m_graphCanvasNodeId;
}
bool ExecutionLogTreeItem::OnMatchesFilter(const DebugLogFilter& treeFilter)
{
bool matches = false;
matches = matches || (m_displayName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_inputName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_outputName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_graphName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_sourceEntityName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_timeString.lastIndexOf(treeFilter.m_filter) >= 0);
return matches;
}
void ExecutionLogTreeItem::ResolveWrapperNode(bool refreshData)
{
if (m_graphCanvasNodeId.IsValid())
{
if (GraphCanvas::GraphUtils::IsWrapperNode(m_graphCanvasNodeId))
{
AZ::EntityId originalNodeId = m_graphCanvasNodeId;
ScriptCanvas::SlotId slotId;
if (HasExecutionInput())
{
slotId = m_inputSlot;
}
if (HasExecutionOutput())
{
slotId = m_outputSlot;
}
GraphCanvas::Endpoint endpoint;
EBusHandlerNodeDescriptorRequestBus::EventResult(endpoint, m_graphCanvasNodeId, &EBusHandlerNodeDescriptorRequests::MapSlotToGraphCanvasEndpoint, slotId);
if (endpoint.IsValid())
{
m_graphCanvasNodeId = endpoint.GetNodeId();
}
if (originalNodeId != m_graphCanvasNodeId && refreshData)
{
ScrapeGraphCanvasData();
}
}
}
}
void ExecutionLogTreeItem::ScrapeBehaviorContextData()
{
if (m_graphName.isEmpty())
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, GetAssetId());
AZStd::string fileName;
AzFramework::StringFunc::Path::GetFileName(assetInfo.m_relativePath.c_str(), fileName);
m_relativeGraphPath = assetInfo.m_relativePath.c_str();
m_graphName = fileName.c_str();
if (m_graphName.isEmpty())
{
m_graphName = "Unknown Canvas";
m_relativeGraphPath = GetAssetId().ToString<AZStd::string>().c_str();
}
}
const NodePaletteModelInformation* modelInformation = nullptr;
GeneralRequestBus::BroadcastResult(modelInformation, &GeneralRequests::FindNodePaletteModelInformation, m_nodeType);
if (modelInformation)
{
const CategoryInformation* categoryInformation = nullptr;
GeneralRequestBus::BroadcastResult(categoryInformation, &GeneralRequests::FindNodePaletteCategoryInformation, modelInformation->m_categoryPath);
m_displayName = QString(modelInformation->m_displayName.c_str());
if (categoryInformation && categoryInformation->m_paletteOverride.compare(GraphCanvas::NodePaletteTreeItem::DefaultNodeTitlePalette) != 0)
{
m_paletteConfiguration.SetColorPalette(categoryInformation->m_paletteOverride);
}
else if (!modelInformation->m_titlePaletteOverride.empty())
{
m_paletteConfiguration.SetColorPalette(modelInformation->m_titlePaletteOverride);
}
else
{
m_paletteConfiguration.SetColorPalette(GraphCanvas::NodePaletteTreeItem::DefaultNodeTitlePalette);
}
}
OnStylesLoaded();
SignalDataChanged();
}
void ExecutionLogTreeItem::ScrapeGraphCanvasData()
{
if (!m_graphCanvasGraphId.IsValid())
{
GeneralRequestBus::BroadcastResult(m_graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, GetAssetId());
if (!EditorGraphNotificationBus::Handler::BusIsConnected())
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::FindScriptCanvasIdByAssetId, GetAssetId());
EditorGraphNotificationBus::Handler::BusConnect(scriptCanvasId);
}
}
if (m_graphCanvasGraphId.IsValid())
{
if (!m_graphCanvasNodeId.IsValid())
{
AssetGraphSceneBus::BroadcastResult(m_scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, GetAssetId(), m_scriptCanvasAssetNodeId);
SceneMemberMappingRequestBus::EventResult(m_graphCanvasNodeId, m_scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
}
if (m_graphCanvasNodeId.IsValid())
{
const bool refreshDisplayData = false;
ResolveWrapperNode(refreshDisplayData);
AZStd::string displayName;
GraphCanvas::NodeTitleRequestBus::EventResult(displayName, m_graphCanvasNodeId, &GraphCanvas::NodeTitleRequests::GetTitle);
if (!displayName.empty())
{
m_displayName = displayName.c_str();
}
GraphCanvas::NodeTitleRequestBus::Event(m_graphCanvasNodeId, &GraphCanvas::NodeTitleRequests::ConfigureIconConfiguration, m_paletteConfiguration);
OnStylesLoaded();
PopulateInputSlotData();
PopulateOutputSlotData();
SignalDataChanged();
}
}
}
void ExecutionLogTreeItem::PopulateInputSlotData()
{
if (m_graphCanvasNodeId.IsValid() && HasExecutionInput())
{
GraphCanvas::SlotId slotId;
SlotMappingRequestBus::EventResult(slotId, m_graphCanvasNodeId, &SlotMappingRequests::MapToGraphCanvasId, m_inputSlot);
AZStd::string inputName;
GraphCanvas::SlotRequestBus::EventResult(inputName, slotId, &GraphCanvas::SlotRequests::GetName);
if (!inputName.empty())
{
m_inputName = inputName.c_str();
}
}
}
void ExecutionLogTreeItem::PopulateOutputSlotData()
{
if (m_graphCanvasNodeId.IsValid() && HasExecutionOutput())
{
GraphCanvas::SlotId slotId;
SlotMappingRequestBus::EventResult(slotId, m_graphCanvasNodeId, &SlotMappingRequests::MapToGraphCanvasId, m_outputSlot);
AZStd::string outputName;
GraphCanvas::SlotRequestBus::EventResult(outputName, slotId, &GraphCanvas::SlotRequests::GetName);
if (!outputName.empty())
{
m_outputName = outputName.c_str();
}
}
}
////////////////////
// DataLogTreeItem
////////////////////
DataLogTreeItem::DataLogTreeItem(const ScriptCanvas::GraphIdentifier& graphIdentifier)
: m_graphIdentifier(graphIdentifier)
{
m_inputName = "---";
m_outputName = "---";
ScrapeData();
}
QVariant DataLogTreeItem::Data(const QModelIndex& index, int role) const
{
switch (index.column())
{
case Column::Input:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
if (m_inputData.isEmpty())
{
return m_inputName;
}
return QString("%1 - (%2)").arg(m_inputName, m_inputData);
}
}
break;
case Column::Output:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
if (m_outputData.isEmpty())
{
return m_outputName;
}
return QString("%1 - (%2)").arg(m_outputName, m_outputData);
}
}
break;
default:
break;
}
return QVariant();
}
void DataLogTreeItem::RegisterDataInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::Endpoint& endpoint, AZStd::string_view slotName, AZStd::string_view dataString)
{
SetIncitingEndpoint(incitingEndpoint);
m_assetInputEndpoint = endpoint;
m_inputName = slotName.data();
m_inputData = dataString.data();
ScrapeInputName();
}
bool DataLogTreeItem::HasInput() const
{
return m_assetInputEndpoint.IsValid();
}
void DataLogTreeItem::RegisterDataOutput(const ScriptCanvas::Endpoint& endpoint, AZStd::string_view slotName, AZStd::string_view dataString)
{
m_assetOutputEndpoint = endpoint;
m_outputName = slotName.data();
m_outputData = dataString.data();
ScrapeOutputName();
}
bool DataLogTreeItem::HasOutput() const
{
return m_assetOutputEndpoint.IsValid();
}
bool DataLogTreeItem::OnMatchesFilter(const DebugLogFilter& treeFilter)
{
bool matches = false;
matches = matches || (m_inputName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_inputData.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_outputName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_outputData.lastIndexOf(treeFilter.m_filter) >= 0);
return matches;
}
AZ::Data::AssetId DataLogTreeItem::GetAssetId() const
{
return m_graphIdentifier.m_assetId;
}
void DataLogTreeItem::ScrapeData()
{
if (!m_graphCanvasGraphId.IsValid())
{
GeneralRequestBus::BroadcastResult(m_graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, m_graphIdentifier.m_assetId);
}
ScrapeInputName();
ScrapeOutputName();
}
void DataLogTreeItem::InvalidateEditorIds()
{
InvalidateGraphCanvasIds();
}
void DataLogTreeItem::InvalidateGraphCanvasIds()
{
m_graphCanvasGraphId.SetInvalid();
}
void DataLogTreeItem::ScrapeInputName()
{
if (m_graphCanvasGraphId.IsValid() && m_assetInputEndpoint.IsValid())
{
AZ::EntityId scriptCanvasNodeId;
AssetGraphSceneBus::BroadcastResult(scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, GetAssetId(), m_assetInputEndpoint.GetNodeId());
GraphCanvas::NodeId graphCanvasNodeId;
SceneMemberMappingRequestBus::EventResult(graphCanvasNodeId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
GraphCanvas::SlotId slotId;
SlotMappingRequestBus::EventResult(slotId, graphCanvasNodeId, &SlotMappingRequests::MapToGraphCanvasId, m_assetInputEndpoint.GetSlotId());
AZStd::string name;
GraphCanvas::SlotRequestBus::EventResult(name, slotId, &GraphCanvas::SlotRequests::GetName);
if (!name.empty())
{
m_inputName = name.c_str();
}
}
}
void DataLogTreeItem::ScrapeOutputName()
{
if (m_graphCanvasGraphId.IsValid() && m_assetOutputEndpoint.IsValid())
{
AZ::EntityId scriptCanvasNodeId;
AssetGraphSceneBus::BroadcastResult(scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, GetAssetId(), m_assetOutputEndpoint.GetNodeId());
GraphCanvas::NodeId graphCanvasNodeId;
SceneMemberMappingRequestBus::EventResult(graphCanvasNodeId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
GraphCanvas::SlotId slotId;
SlotMappingRequestBus::EventResult(slotId, graphCanvasNodeId, &SlotMappingRequests::MapToGraphCanvasId, m_assetOutputEndpoint.GetSlotId());
AZStd::string name;
GraphCanvas::SlotRequestBus::EventResult(name, slotId, &GraphCanvas::SlotRequests::GetName);
if (!name.empty())
{
m_outputName = name.c_str();
}
}
}
bool DataLogTreeItem::LessThan(const GraphCanvas::GraphCanvasTreeItem* graphItem) const
{
return !azrtti_istypeof<const NodeAnnotationTreeItem*>(graphItem);
}
///////////////////////////
// NodeAnnotationTreeItem
///////////////////////////
NodeAnnotationTreeItem::NodeAnnotationTreeItem()
: m_annotationLevel(ScriptCanvas::AnnotateNodeSignal::AnnotationLevel::Info)
{
}
NodeAnnotationTreeItem::NodeAnnotationTreeItem(ScriptCanvas::AnnotateNodeSignal::AnnotationLevel annotationLevel, const AZStd::string& annotation)
: m_annotationLevel(annotationLevel)
, m_annotation(annotation.c_str())
{
switch (m_annotationLevel)
{
case ScriptCanvas::AnnotateNodeSignal::AnnotationLevel::Info:
m_annotationIcon = QIcon(":/ScriptCanvasEditorResources/Resources/message_icon.png");
break;
case ScriptCanvas::AnnotateNodeSignal::AnnotationLevel::Warning:
m_annotationIcon = QIcon(":/ScriptCanvasEditorResources/Resources/warning_symbol.png");
break;
case ScriptCanvas::AnnotateNodeSignal::AnnotationLevel::Error:
m_annotationIcon = QIcon(":/ScriptCanvasEditorResources/Resources/error_icon.png");
break;
default:
break;
}
}
QVariant NodeAnnotationTreeItem::Data(const QModelIndex& index, int role) const
{
// We are spanned, we we only have a single column
if (index.column() == DebugLogTreeItem::Column::NodeName)
{
switch (role)
{
case Qt::DecorationRole:
{
return m_annotationIcon;
}
case Qt::DisplayRole:
{
return m_annotation;
}
case Qt::ToolTipRole:
{
return m_annotation;
}
default:
break;
}
}
return QVariant();
}
bool NodeAnnotationTreeItem::OnMatchesFilter(const DebugLogFilter& treeFilter)
{
bool matches = false;
matches = matches || (m_annotation.lastIndexOf(treeFilter.m_filter) >= 0);
return matches;
}
}
@@ -0,0 +1,288 @@
/*
* 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 <AzCore/PlatformDef.h>
// qdatetime.h(331): warning C4251: 'QDateTime::d': class 'QSharedDataPointer<QDateTimePrivate>' needs to have dll-interface to be used by clients of class 'QDateTime'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <QIcon>
#include <QTime>
#include <QTimer>
AZ_POP_DISABLE_WARNING
#include <AzCore/Component/NamedEntityId.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/chrono/chrono.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/Core/Endpoint.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingTypes.h>
#include <Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h>
namespace ScriptCanvasEditor
{
class DebugLogFilter
{
public:
QRegExp m_filter;
bool IsEmpty() const
{
return m_filter.isEmpty();
}
};
class DebugLogTreeItem
: public GraphCanvas::GraphCanvasTreeItem
{
public:
AZ_CLASS_ALLOCATOR(DebugLogTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(DebugLogTreeItem, "{E0B2A52B-47A4-40FF-A76F-4655125D01CC}", GraphCanvas::GraphCanvasTreeItem);
enum Column
{
IndexForce = -1,
NodeName,
Input,
Output,
TimeStep,
ScriptName,
SourceEntity,
Count
};
bool MatchesFilter(const DebugLogFilter& treeFilter);
const ScriptCanvas::Endpoint& GetIncitingEndpoint() const;
bool IsTriggeredBy(const ScriptCanvas::Endpoint& endpoint) const;
Qt::ItemFlags Flags(const QModelIndex& index) const override final;
int GetColumnCount() const override final;
protected:
void SetIncitingEndpoint(const ScriptCanvas::Endpoint& endpoint);
virtual bool OnMatchesFilter(const DebugLogFilter& treeFilter) = 0;
private:
ScriptCanvas::Endpoint m_incitingEndpoint;
};
class ExecutionLogTreeItem;
class DebugLogRootItem
: public DebugLogTreeItem
{
public:
enum UpdatePolicy
{
RealTime,
Batched,
SingleTime
};
AZ_CLASS_ALLOCATOR(DebugLogRootItem, AZ::SystemAllocator, 0);
AZ_RTTI(DebugLogRootItem, "{CF59F72E-04AC-415C-A2F2-99D79564730B}", DebugLogTreeItem);
DebugLogRootItem();
~DebugLogRootItem();
ExecutionLogTreeItem* CreateExecutionItem(const LoggingDataId& loggingDataId, const ScriptCanvas::NodeTypeIdentifier& nodeType, const ScriptCanvas::GraphInfo& graphInfo, const ScriptCanvas::NamedNodeId& nodeId);
QVariant Data(const QModelIndex& index, int role) const override final;
void ResetData();
void SetUpdatePolicy(UpdatePolicy updatePolicy);
UpdatePolicy GetUpdatePolicy() const;
void RedoLayout();
protected:
bool OnMatchesFilter([[maybe_unused]] const DebugLogFilter& treeFilter) { return true; }
UpdatePolicy m_updatePolicy;
QTimer m_additionTimer;
};
class ExecutionLogTreeItem
: public DebugLogTreeItem
, public GraphCanvas::StyleManagerNotificationBus::Handler
, public EditorGraphNotificationBus::Handler
, public GeneralAssetNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ExecutionLogTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(ExecutionLogTreeItem, "{71139142-A30C-4A16-81CC-D51314AEAF7D}", DebugLogTreeItem);
ExecutionLogTreeItem(const LoggingDataId& loggingDataId, const ScriptCanvas::NodeTypeIdentifier& nodeType, const ScriptCanvas::GraphInfo& graphInfo, const ScriptCanvas::NamedNodeId& nodeId);
~ExecutionLogTreeItem() override = default;
QVariant Data(const QModelIndex& index, int role) const override final;
AZ::EntityId GetNodeId() const;
void RegisterAnnotation(const ScriptCanvas::AnnotateNodeSignal& annotationSignal, bool allowAddSignal);
void RegisterDataInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::string_view dataString, bool allowAddSignal);
void RegisterDataOutput(const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::string_view dataString, bool allowAddSignal);
void RegisterExecutionInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::chrono::milliseconds relativeExecution);
bool HasExecutionInput() const;
void RegisterExecutionOutput(const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::chrono::milliseconds relativeExecution);
bool HasExecutionOutput() const;
// GraphCanvas::StyleManagerNotificationBus
void OnStylesUnloaded() override;
void OnStylesLoaded() override;
////
// GeneralNotificationsBus
void OnAssetVisualized() override;
void OnAssetUnloaded() override;
////
// EditorGraphNotificationBus
void OnGraphCanvasSceneDisplayed() override;
////
const ScriptCanvas::GraphIdentifier& GetGraphIdentifier() const;
const AZ::Data::AssetId& GetAssetId() const;
AZ::EntityId GetScriptCanvasAssetNodeId() const;
GraphCanvas::NodeId GetGraphCanvasNodeId() const;
protected:
bool OnMatchesFilter(const DebugLogFilter& treeFilter) override;
private:
void ResolveWrapperNode(bool refreshData = true);
void ScrapeBehaviorContextData();
void ScrapeGraphCanvasData();
void PopulateInputSlotData();
void PopulateOutputSlotData();
LoggingDataId m_loggingDataId;
ScriptCanvas::NodeTypeIdentifier m_nodeType;
ScriptCanvas::GraphInfo m_graphInfo;
QString m_sourceEntityName;
QString m_graphName;
QString m_relativeGraphPath;
AZ::EntityId m_graphCanvasGraphId;
AZ::EntityId m_scriptCanvasAssetNodeId;
AZ::EntityId m_scriptCanvasNodeId;
GraphCanvas::NodeId m_graphCanvasNodeId;
QString m_displayName;
ScriptCanvas::SlotId m_inputSlot;
QString m_inputName;
ScriptCanvas::SlotId m_outputSlot;
QString m_outputName;
QString m_timeString;
GraphCanvas::PaletteIconConfiguration m_paletteConfiguration;
const QPixmap* m_iconPixmap;
};
class DataLogTreeItem
: public DebugLogTreeItem
{
friend class ExecutionLogTreeItem;
public:
AZ_CLASS_ALLOCATOR(DataLogTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(DataLogTreeItem, "{04D997AD-E3CA-47CA-9810-8814B36AB726}", DebugLogTreeItem);
DataLogTreeItem(const ScriptCanvas::GraphIdentifier& graphIdentifier);
QVariant Data(const QModelIndex& index, int role) const override final;
void RegisterDataInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::Endpoint& endpoint, AZStd::string_view slotName, AZStd::string_view data);
bool HasInput() const;
void RegisterDataOutput(const ScriptCanvas::Endpoint& endpoint, AZStd::string_view slotName, AZStd::string_view dataString);
bool HasOutput() const;
protected:
bool OnMatchesFilter(const DebugLogFilter& treeFilter) override;
bool LessThan(const GraphCanvas::GraphCanvasTreeItem* graphItem) const override;
private:
AZ::Data::AssetId GetAssetId() const;
void ScrapeData();
void InvalidateEditorIds();
void InvalidateGraphCanvasIds();
void ScrapeInputName();
void ScrapeOutputName();
ScriptCanvas::GraphIdentifier m_graphIdentifier;
GraphCanvas::GraphId m_graphCanvasGraphId;
ScriptCanvas::Endpoint m_assetInputEndpoint;
QString m_inputName;
QString m_inputData;
ScriptCanvas::Endpoint m_assetOutputEndpoint;
QString m_outputName;
QString m_outputData;
};
class NodeAnnotationTreeItem
: public DebugLogTreeItem
{
public:
AZ_CLASS_ALLOCATOR(NodeAnnotationTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(NodeAnnotationTreeItem, "{4A052945-F8D1-4A96-8D52-D8C20504E30F}", DebugLogTreeItem);
NodeAnnotationTreeItem();
NodeAnnotationTreeItem(ScriptCanvas::AnnotateNodeSignal::AnnotationLevel annotationLevel, const AZStd::string& annotation);
~NodeAnnotationTreeItem() override = default;
QVariant Data(const QModelIndex& index, int role) const override final;
protected:
bool OnMatchesFilter(const DebugLogFilter& treeFilter) override;
private:
ScriptCanvas::AnnotateNodeSignal::AnnotationLevel m_annotationLevel;
QString m_annotation;
QIcon m_annotationIcon;
};
}
@@ -0,0 +1,345 @@
/*
* 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 <Editor/View/Widgets/LoggingPanel/PivotTree/EntityPivotTree/EntityPivotTree.h>
namespace ScriptCanvasEditor
{
/////////////////////////////
// EntityPivotTreeGraphItem
/////////////////////////////
EntityPivotTreeGraphItem::EntityPivotTreeGraphItem(const ScriptCanvas::GraphIdentifier& graphIdentifier)
: PivotTreeGraphItem(graphIdentifier.m_assetId)
, m_checkState(Qt::CheckState::Unchecked)
, m_graphIdentifier(graphIdentifier)
{
}
Qt::CheckState EntityPivotTreeGraphItem::GetCheckState() const
{
return m_checkState;
}
void EntityPivotTreeGraphItem::SetCheckState(Qt::CheckState checkState)
{
m_checkState = checkState;
SignalDataChanged();
}
const ScriptCanvas::GraphIdentifier& EntityPivotTreeGraphItem::GetGraphIdentifier() const
{
return m_graphIdentifier;
}
//////////////////////////////
// EntityPivotTreeEntityItem
//////////////////////////////
EntityPivotTreeEntityItem::EntityPivotTreeEntityItem(const AZ::NamedEntityId& entityId)
: PivotTreeEntityItem(entityId)
, m_checkState(Qt::CheckState::Unchecked)
{
const bool isPivotElement = true;
SetIsPivotedElement(isPivotElement);
}
void EntityPivotTreeEntityItem::RegisterGraphIdentifier(const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_pivotItems.find(graphIdentifier);
if (mapIter == m_pivotItems.end())
{
EntityPivotTreeGraphItem* graphItem = CreateChildNode<EntityPivotTreeGraphItem>(graphIdentifier);
m_pivotItems[graphIdentifier] = graphItem;
if (m_checkState != Qt::CheckState::PartiallyChecked)
{
graphItem->SetCheckState(m_checkState);
}
}
}
void EntityPivotTreeEntityItem::OnChildDataChanged(GraphCanvasTreeItem* treeItem)
{
EntityPivotTreeGraphItem* graphItem = static_cast<EntityPivotTreeGraphItem*>(treeItem);
ScriptCanvas::GraphIdentifier graphIdentifier = graphItem->GetGraphIdentifier();
if (graphItem->GetCheckState() == Qt::CheckState::Checked)
{
LoggingDataRequestBus::Event(GetLoggingDataId(), &LoggingDataRequests::EnableRegistration, GetNamedEntityId(), graphIdentifier);
}
else
{
LoggingDataRequestBus::Event(GetLoggingDataId(), &LoggingDataRequests::DisableRegistration, GetNamedEntityId(), graphIdentifier);
}
CalculateCheckState();
SignalDataChanged();
}
void EntityPivotTreeEntityItem::UnregisterGraphIdentifier(const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_pivotItems.find(graphIdentifier);
if (mapIter != m_pivotItems.end())
{
RemoveChild(mapIter->second);
m_pivotItems.erase(mapIter);
}
}
Qt::CheckState EntityPivotTreeEntityItem::GetCheckState() const
{
return m_checkState;
}
void EntityPivotTreeEntityItem::SetCheckState(Qt::CheckState checkState)
{
if (m_checkState != checkState)
{
m_checkState = checkState;
for (const auto& mapIter : m_pivotItems)
{
mapIter.second->SetCheckState(checkState);
}
SignalDataChanged();
}
}
EntityPivotTreeGraphItem* EntityPivotTreeEntityItem::FindGraphTreeItem(const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_pivotItems.find(graphIdentifier);
if (mapIter != m_pivotItems.end())
{
return mapIter->second;
}
else
{
return nullptr;
}
}
void EntityPivotTreeEntityItem::OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (namedEntityId == GetEntityId())
{
EntityPivotTreeGraphItem* item = FindGraphTreeItem(graphIdentifier);
if (item)
{
if (isEnabled)
{
item->SetCheckState(Qt::CheckState::Checked);
}
else
{
item->SetCheckState(Qt::CheckState::Unchecked);
}
}
}
}
void EntityPivotTreeEntityItem::OnLoggingDataIdSet()
{
LoggingDataNotificationBus::Handler::BusDisconnect();
LoggingDataNotificationBus::Handler::BusConnect(GetLoggingDataId());
}
void EntityPivotTreeEntityItem::CalculateCheckState()
{
bool isChecked = false;
bool isUnchecked = false;
for (const auto& mapIter : m_pivotItems)
{
if (mapIter.second->GetCheckState() == Qt::CheckState::Checked)
{
isChecked = true;
}
else
{
isUnchecked = true;
}
if (isChecked && isUnchecked)
{
break;
}
}
m_checkState = Qt::CheckState::Unchecked;
if (isChecked && isUnchecked)
{
m_checkState = Qt::CheckState::PartiallyChecked;
}
else if (isChecked)
{
m_checkState = Qt::CheckState::Checked;
}
}
////////////////////////
// EntityPivotTreeRoot
////////////////////////
EntityPivotTreeRoot::EntityPivotTreeRoot()
: m_capturingData(false)
{
}
void EntityPivotTreeRoot::OnDataSourceChanged(const LoggingDataId& aggregateDataSource)
{
ClearData();
LoggingDataNotificationBus::Handler::BusDisconnect();
m_dataSource = aggregateDataSource;
const LoggingDataAggregator* dataAggregator = nullptr;
LoggingDataRequestBus::EventResult(dataAggregator, m_dataSource, &LoggingDataRequests::FindLoggingData);
if (dataAggregator)
{
const EntityGraphRegistrationMap& registrationMap = dataAggregator->GetEntityGraphRegistrationMap();
for (const auto& mapIter : registrationMap)
{
OnEntityGraphRegistered(mapIter.first, mapIter.second);
}
if (dataAggregator->IsCapturingData())
{
OnDataCaptureBegin();
}
}
LoggingDataNotificationBus::Handler::BusConnect(m_dataSource);
}
void EntityPivotTreeRoot::OnDataCaptureBegin()
{
m_capturingData = true;
}
void EntityPivotTreeRoot::OnDataCaptureEnd()
{
m_capturingData = false;
for (const auto& unregistrationPair : m_delayedUnregistrations)
{
OnEntityGraphUnregistered(unregistrationPair.first, unregistrationPair.second);
}
m_delayedUnregistrations.clear();
}
void EntityPivotTreeRoot::OnEntityGraphRegistered(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
EntityPivotTreeEntityItem* pivotItem = nullptr;
auto mapIter = m_entityTreeItemMapping.find(namedEntityId);
if (mapIter != m_entityTreeItemMapping.end())
{
pivotItem = mapIter->second;
}
else
{
pivotItem = CreateChildNode<EntityPivotTreeEntityItem>(namedEntityId);
m_entityTreeItemMapping[namedEntityId] = pivotItem;
}
if (pivotItem)
{
pivotItem->RegisterGraphIdentifier(graphIdentifier);
}
}
void EntityPivotTreeRoot::OnEntityGraphUnregistered(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_entityTreeItemMapping.find(namedEntityId);
if (mapIter != m_entityTreeItemMapping.end())
{
EntityPivotTreeEntityItem* pivotItem = mapIter->second;
if (!m_capturingData)
{
pivotItem->UnregisterGraphIdentifier(graphIdentifier);
if (pivotItem->GetChildCount() == 0)
{
RemoveChild(pivotItem);
m_entityTreeItemMapping.erase(mapIter);
}
}
else
{
m_delayedUnregistrations.emplace_back(namedEntityId, graphIdentifier);
}
}
}
void EntityPivotTreeRoot::OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
return;
}
auto mapIter = m_entityTreeItemMapping.find(namedEntityId);
if (mapIter != m_entityTreeItemMapping.end())
{
EntityPivotTreeGraphItem* pivotTreeItem = mapIter->second->FindGraphTreeItem(graphIdentifier);
if (pivotTreeItem)
{
if (isEnabled)
{
pivotTreeItem->SetCheckState(Qt::CheckState::Checked);
}
else
{
pivotTreeItem->SetCheckState(Qt::CheckState::Unchecked);
}
}
}
}
void EntityPivotTreeRoot::ClearData()
{
ClearChildren();
m_entityTreeItemMapping.clear();
}
//////////////////////////
// EntityPivotTreeWidget
//////////////////////////
EntityPivotTreeWidget::EntityPivotTreeWidget(QWidget* parent)
: PivotTreeWidget(aznew EntityPivotTreeRoot(), AZ_CRC("EntityPivotTreeId", 0xd44255d6), parent)
{
}
#include <Editor/View/Widgets/LoggingPanel/PivotTree/EntityPivotTree/moc_EntityPivotTree.cpp>
}
@@ -0,0 +1,124 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/std/containers/unordered_map.h>
#include <Editor/View/Widgets/LoggingPanel/PivotTree/PivotTreeWidget.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#endif
namespace ScriptCanvasEditor
{
class EntityPivotTreeGraphItem
: public PivotTreeGraphItem
{
friend class EntityPivotTreeEntityItem;
public:
AZ_CLASS_ALLOCATOR(EntityPivotTreeGraphItem, AZ::SystemAllocator, 0);
AZ_RTTI(EntityPivotTreeGraphItem, "{CE064D69-D478-4594-A596-5DBE0DE46F6E}", PivotTreeGraphItem);
EntityPivotTreeGraphItem(const ScriptCanvas::GraphIdentifier& graphIdentifier);
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState checkState) override final;
const ScriptCanvas::GraphIdentifier& GetGraphIdentifier() const;
private:
Qt::CheckState m_checkState;
ScriptCanvas::GraphIdentifier m_graphIdentifier;
};
class EntityPivotTreeEntityItem
: public PivotTreeEntityItem
, public LoggingDataNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(EntityPivotTreeEntityItem, AZ::SystemAllocator, 0);
AZ_RTTI(EntityPivotTreeEntityItem, "{027A8617-4095-46F1-B9AD-49E360C90C73}", PivotTreeEntityItem);
EntityPivotTreeEntityItem(const AZ::NamedEntityId& entityId);
void RegisterGraphIdentifier(const ScriptCanvas::GraphIdentifier& graphIdentifier);
void UnregisterGraphIdentifier(const ScriptCanvas::GraphIdentifier& graphIdentifier);
void OnChildDataChanged(GraphCanvasTreeItem* treeItem) override;
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState debugging) override final;
EntityPivotTreeGraphItem* FindGraphTreeItem(const ScriptCanvas::GraphIdentifier& registrationData);
// LoggingDataNotificationBus
void OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& registrationData) override;
////
protected:
void OnLoggingDataIdSet() override;
private:
void CalculateCheckState();
Qt::CheckState m_checkState;
AZStd::unordered_map< ScriptCanvas::GraphIdentifier, EntityPivotTreeGraphItem*> m_pivotItems;
};
class EntityPivotTreeRoot
: public PivotTreeRoot
, public LoggingDataNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(EntityPivotTreeRoot, AZ::SystemAllocator, 0);
AZ_RTTI(EntityPivotTreeRoot, "{93DE206E-CE31-4A59-BEBF-87B26E5A28D2}", PivotTreeRoot);
EntityPivotTreeRoot();
// PivotTreeRoot
void OnDataSourceChanged(const LoggingDataId& aggregateDataSource) override;
////
// LoggedDataNotifications
void OnDataCaptureBegin() override;
void OnDataCaptureEnd() override;
void OnEntityGraphRegistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& assetId) override;
void OnEntityGraphUnregistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& assetId) override;
void OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& registrationData) override;
////
private:
void ClearData();
LoggingDataId m_dataSource;
AZStd::unordered_map< AZ::EntityId, EntityPivotTreeEntityItem* > m_entityTreeItemMapping;
AZStd::vector < AZStd::pair < AZ::NamedEntityId, ScriptCanvas::GraphIdentifier > > m_delayedUnregistrations;
bool m_capturingData;
};
class EntityPivotTreeWidget
: public PivotTreeWidget
{
Q_OBJECT
public:
EntityPivotTreeWidget(QWidget* parent);
};
}
@@ -0,0 +1,583 @@
/*
* 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 <AzCore/Asset/AssetManagerBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <Editor/View/Widgets/LoggingPanel/PivotTree/GraphPivotTree/GraphPivotTree.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
namespace ScriptCanvasEditor
{
/////////////////////////////
// GraphPivotTreeEntityItem
/////////////////////////////
GraphPivotTreeEntityItem::GraphPivotTreeEntityItem(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
: PivotTreeEntityItem(namedEntityId)
, m_checkState(Qt::CheckState::Unchecked)
, m_graphIdentifier(graphIdentifier)
{
}
Qt::CheckState GraphPivotTreeEntityItem::GetCheckState() const
{
return m_checkState;
}
void GraphPivotTreeEntityItem::SetCheckState(Qt::CheckState checkState)
{
m_checkState = checkState;
SignalDataChanged();
}
const ScriptCanvas::GraphIdentifier& GraphPivotTreeEntityItem::GetGraphIdentifier() const
{
return m_graphIdentifier;
}
////////////////////////////
// GraphPivotTreeGraphItem
////////////////////////////
GraphPivotTreeGraphItem::GraphPivotTreeGraphItem(const AZ::Data::AssetId& assetId)
: PivotTreeGraphItem(assetId)
, m_checkState(Qt::CheckState::Unchecked)
{
const bool isPivotedElement = true;
SetIsPivotedElement(isPivotedElement);
const bool isChecked = true;
SetupDynamicallySpawnedElementItem(isChecked);
}
void GraphPivotTreeGraphItem::OnDataSwitch()
{
bool isChecked = true;
auto pivotIter = m_pivotItems.find(AZ::EntityId());
if (pivotIter != m_pivotItems.end())
{
GraphPivotTreeEntityItem* entityItem = pivotIter->second;
isChecked = entityItem->GetCheckState() == Qt::CheckState::Checked;
}
m_pivotItems.clear();
ClearChildren();
SetupDynamicallySpawnedElementItem(isChecked);
}
void GraphPivotTreeGraphItem::RegisterEntity(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto pivotRange = m_pivotItems.equal_range(entityId);
bool foundElement = false;
for (auto mapIter = pivotRange.first; mapIter != pivotRange.second; ++mapIter)
{
if (mapIter->second->GetGraphIdentifier() == graphIdentifier)
{
foundElement = true;
break;
}
}
if (!foundElement)
{
GraphPivotTreeEntityItem* entityItem = CreateChildNode<GraphPivotTreeEntityItem>(entityId, graphIdentifier);
entityItem->SetCheckState(Qt::CheckState::Unchecked);
m_pivotItems.insert(AZStd::make_pair(entityId,entityItem));
}
}
void GraphPivotTreeGraphItem::UnregisterEntity(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto pivotRange = m_pivotItems.equal_range(entityId);
for (auto pivotIter = pivotRange.first; pivotIter != pivotRange.second; ++pivotIter)
{
if (pivotIter->second->GetGraphIdentifier() == graphIdentifier)
{
RemoveChild(pivotIter->second);
m_pivotItems.erase(pivotIter);
break;
}
}
}
void GraphPivotTreeGraphItem::OnChildDataChanged(GraphCanvasTreeItem* treeItem)
{
GraphPivotTreeEntityItem* graphItem = static_cast<GraphPivotTreeEntityItem*>(treeItem);
if (graphItem->GetCheckState() == Qt::CheckState::Checked)
{
LoggingDataRequestBus::Event(GetLoggingDataId(), &LoggingDataRequests::EnableRegistration, graphItem->GetNamedEntityId(), graphItem->GetGraphIdentifier());
}
else
{
LoggingDataRequestBus::Event(GetLoggingDataId(), &LoggingDataRequests::DisableRegistration, graphItem->GetNamedEntityId(), graphItem->GetGraphIdentifier());
}
CalculateCheckState();
SignalDataChanged();
}
Qt::CheckState GraphPivotTreeGraphItem::GetCheckState() const
{
return m_checkState;
}
void GraphPivotTreeGraphItem::SetCheckState(Qt::CheckState checkState)
{
if (checkState != m_checkState)
{
m_checkState = checkState;
for (int i = 0; i < GetChildCount(); ++i)
{
PivotTreeItem* treeItem = static_cast<PivotTreeItem*>(FindChildByRow(i));
if (treeItem)
{
treeItem->SetCheckState(checkState);
}
}
SignalDataChanged();
}
}
GraphPivotTreeEntityItem* GraphPivotTreeGraphItem::FindDynamicallySpawnedTreeItem() const
{
return FindEntityTreeItem(AZ::NamedEntityId(AZ::EntityId(), ""), ScriptCanvas::GraphIdentifier(GetAssetId(), k_dynamicallySpawnedControllerId));
}
GraphPivotTreeEntityItem* GraphPivotTreeGraphItem::FindEntityTreeItem(const AZ::NamedEntityId& namedEntityId, [[maybe_unused]] const ScriptCanvas::GraphIdentifier& graphIdentifier) const
{
auto pivotIter = m_pivotItems.find(namedEntityId);
if (pivotIter != m_pivotItems.end())
{
return pivotIter->second;
}
return nullptr;
}
void GraphPivotTreeGraphItem::OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_assetId == GetAssetId())
{
GraphPivotTreeEntityItem* item = FindEntityTreeItem(namedEntityId, graphIdentifier);
if (item)
{
if (isEnabled)
{
item->SetCheckState(Qt::CheckState::Checked);
}
else
{
item->SetCheckState(Qt::CheckState::Unchecked);
}
}
}
}
void GraphPivotTreeGraphItem::OnLoggingDataIdSet()
{
LoggingDataNotificationBus::Handler::BusDisconnect();
LoggingDataNotificationBus::Handler::BusConnect(GetLoggingDataId());
}
void GraphPivotTreeGraphItem::SetupDynamicallySpawnedElementItem([[maybe_unused]] bool isChecked)
{
AZ::NamedEntityId dynamicEntityId(AZ::EntityId(), "All Graph Instances");
RegisterEntity(dynamicEntityId, ScriptCanvas::GraphIdentifier(GetAssetId(), k_dynamicallySpawnedControllerId));
}
void GraphPivotTreeGraphItem::CalculateCheckState()
{
bool isChecked = false;
bool isUnchecked = false;
for (int i = 0; i < GetChildCount(); ++i)
{
PivotTreeItem* treeItem = static_cast<PivotTreeItem*>(FindChildByRow(i));
if (treeItem)
{
if (treeItem->GetCheckState() == Qt::CheckState::Checked)
{
isChecked = true;
}
else
{
isUnchecked = true;
}
if (isChecked && isUnchecked)
{
break;
}
}
}
if (isChecked && isUnchecked)
{
m_checkState = Qt::CheckState::PartiallyChecked;
}
else if (isChecked)
{
m_checkState = Qt::CheckState::Checked;
}
else
{
m_checkState = Qt::CheckState::Unchecked;
}
}
/////////////////////////
// GraphPivotTreeFolder
/////////////////////////
GraphPivotTreeFolder::GraphPivotTreeFolder(AZStd::string_view folder)
: m_folderName(folder)
, m_checkState(Qt::CheckState::Unchecked)
{
const bool isPivotElement = true;
SetIsPivotedElement(isPivotElement);
}
Qt::CheckState GraphPivotTreeFolder::GetCheckState() const
{
return m_checkState;
}
void GraphPivotTreeFolder::SetCheckState(Qt::CheckState checkState)
{
if (m_checkState != checkState)
{
m_checkState = checkState;
for (int i = 0; i < GetChildCount(); ++i)
{
PivotTreeItem* pivotTreeItem = static_cast<PivotTreeItem*>(FindChildByRow(i));
pivotTreeItem->SetCheckState(checkState);
}
SignalDataChanged();
}
}
AZStd::string GraphPivotTreeFolder::GetDisplayName() const
{
return m_folderName;
}
void GraphPivotTreeFolder::OnChildDataChanged([[maybe_unused]] GraphCanvasTreeItem* treeItem)
{
CalculateCheckState();
SignalDataChanged();
}
void GraphPivotTreeFolder::CalculateCheckState()
{
bool isChecked = false;
bool isUnchecked = false;
for (int i = 0; i < GetChildCount(); ++i)
{
const PivotTreeItem* pivotTreeItem = static_cast<const PivotTreeItem*>(FindChildByRow(i));
if (pivotTreeItem)
{
Qt::CheckState checkState = pivotTreeItem->GetCheckState();
if (checkState == Qt::CheckState::PartiallyChecked)
{
isChecked = true;
isUnchecked = true;
}
else if (checkState == Qt::CheckState::Checked)
{
isChecked = true;
}
else
{
isUnchecked = true;
}
if (isChecked && isUnchecked)
{
break;
}
}
}
if (isChecked && isUnchecked)
{
m_checkState = Qt::CheckState::PartiallyChecked;
}
else if (isChecked)
{
m_checkState = Qt::CheckState::Checked;
}
else
{
m_checkState = Qt::CheckState::Unchecked;
}
SignalDataChanged();
}
///////////////////////
// GraphPivotTreeRoot
///////////////////////
GraphPivotTreeRoot::GraphPivotTreeRoot()
: m_categorizer((*this))
{
AzToolsFramework::AssetBrowser::AssetBrowserModel* assetBrowserModel = nullptr;
AzToolsFramework::AssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(assetBrowserModel, &AzToolsFramework::AssetBrowser::AssetBrowserComponentRequests::GetAssetBrowserModel);
m_assetModel = new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel();
AzToolsFramework::AssetBrowser::AssetGroupFilter* assetFilter = new AzToolsFramework::AssetBrowser::AssetGroupFilter();
assetFilter->SetAssetGroup(ScriptCanvasEditor::ScriptCanvasAsset::Description::GetGroup(azrtti_typeid<ScriptCanvasAsset>()));
assetFilter->SetFilterPropagation(AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter::PropagateDirection::Down);
QObject::connect(m_assetModel, &QAbstractItemModel::rowsInserted, this, &GraphPivotTreeRoot::OnScriptCanvasGraphAssetAdded);
QObject::connect(m_assetModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &GraphPivotTreeRoot::OnScriptCanvasGraphAssetRemoved);
m_assetModel->setSourceModel(assetBrowserModel);
SetAllowPruneOnEmpty(false);
}
void GraphPivotTreeRoot::OnDataSourceChanged(const LoggingDataId& aggregateDataSource)
{
if (LoggingDataNotificationBus::Handler::BusIsConnected())
{
LoggingDataNotificationBus::Handler::BusDisconnect();
}
m_loggedDataId = aggregateDataSource;
for (auto assetPair : m_graphTreeItemMapping)
{
assetPair.second->OnDataSwitch();
}
const LoggingDataAggregator* dataAggregator = nullptr;
LoggingDataRequestBus::EventResult(dataAggregator, m_loggedDataId, &LoggingDataRequests::FindLoggingData);
if (dataAggregator)
{
const EntityGraphRegistrationMap& entityPivoting = dataAggregator->GetEntityGraphRegistrationMap();
for (const auto& registrationMap : entityPivoting)
{
OnEntityGraphUnregistered(registrationMap.first, registrationMap.second);
}
if (dataAggregator->IsCapturingData())
{
OnDataCaptureBegin();
}
}
LoggingDataNotificationBus::Handler::BusConnect(m_loggedDataId);
}
void GraphPivotTreeRoot::OnDataCaptureBegin()
{
}
void GraphPivotTreeRoot::OnDataCaptureEnd()
{
}
void GraphPivotTreeRoot::OnEntityGraphRegistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
GraphPivotTreeGraphItem* graphItem = nullptr;
auto mapPairIter = m_graphTreeItemMapping.find(graphIdentifier.m_assetId);
if (mapPairIter == m_graphTreeItemMapping.end())
{
AZStd::string fullPath;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(fullPath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, graphIdentifier.m_assetId);
GraphCanvasTreeItem* parentItem = m_categorizer.GetCategoryNode(fullPath.c_str(), this);
graphItem = parentItem->CreateChildNode<GraphPivotTreeGraphItem>(graphIdentifier.m_assetId);
m_graphTreeItemMapping[graphIdentifier.m_assetId] = graphItem;
}
else
{
graphItem = mapPairIter->second;
}
if (entityId.IsValid())
{
graphItem->RegisterEntity(entityId, graphIdentifier);
}
}
void GraphPivotTreeRoot::OnEntityGraphUnregistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapPairIter = m_graphTreeItemMapping.find(graphIdentifier.m_assetId);
if (mapPairIter != m_graphTreeItemMapping.end())
{
mapPairIter->second->UnregisterEntity(entityId, graphIdentifier);
if (mapPairIter->second->GetChildCount() == 0)
{
m_categorizer.PruneEmptyNodes();
}
}
}
void GraphPivotTreeRoot::OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_graphTreeItemMapping.find(graphIdentifier.m_assetId);
if (mapIter != m_graphTreeItemMapping.end())
{
GraphPivotTreeEntityItem* pivotTreeItem = nullptr;
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
pivotTreeItem = mapIter->second->FindDynamicallySpawnedTreeItem();
}
else
{
pivotTreeItem = mapIter->second->FindEntityTreeItem(namedEntityId, graphIdentifier);
}
if (pivotTreeItem)
{
if (isEnabled)
{
pivotTreeItem->SetCheckState(Qt::CheckState::Checked);
}
else
{
pivotTreeItem->SetCheckState(Qt::CheckState::Unchecked);
}
}
}
}
GraphCanvas::GraphCanvasTreeItem* GraphPivotTreeRoot::CreateCategoryNode([[maybe_unused]] AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvasTreeItem* parent) const
{
return parent->CreateChildNode<GraphPivotTreeFolder>(categoryName);
}
void GraphPivotTreeRoot::OnScriptCanvasGraphAssetAdded(const QModelIndex& parentIndex, int first, int last)
{
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = m_assetModel->index(i, 0, parentIndex);
QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessEntry(entry);
}
}
void GraphPivotTreeRoot::OnScriptCanvasGraphAssetRemoved(const QModelIndex& parentIndex, int first, int last)
{
// TODO: This likely needs to be handled better
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = m_assetModel->index(i, 0, parentIndex);
QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
if (entry && entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
if (productEntry->GetAssetType() == azrtti_typeid<ScriptCanvasEditor::ScriptCanvasAsset>())
{
auto mapIter = m_graphTreeItemMapping.find(productEntry->GetAssetId());
if (mapIter != m_graphTreeItemMapping.end() && mapIter->second)
{
GraphCanvas::GraphCanvasTreeItem* currentItem = mapIter->second;
currentItem->ClearChildren();
m_graphTreeItemMapping.erase(mapIter);
m_categorizer.PruneNode(currentItem);
}
OnEntityGraphUnregistered(AZ::NamedEntityId(), ScriptCanvas::GraphIdentifier(productEntry->GetAssetId(), k_dynamicallySpawnedControllerId));
}
}
}
}
void GraphPivotTreeRoot::ProcessEntry(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
if (entry && entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = static_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
if (productEntry->GetAssetType() == azrtti_typeid<ScriptCanvasEditor::ScriptCanvasAsset>())
{
OnEntityGraphRegistered(AZ::NamedEntityId(), ScriptCanvas::GraphIdentifier(productEntry->GetAssetId(), k_dynamicallySpawnedControllerId));
}
}
}
void GraphPivotTreeRoot::TraverseTree(QModelIndex index)
{
QModelIndex sourceIndex = m_assetModel->mapToSource(index);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessEntry(entry);
int rowCount = m_assetModel->rowCount(index);
for (int i = 0; i < rowCount; ++i)
{
QModelIndex nextIndex = m_assetModel->index(i, 0, index);
TraverseTree(nextIndex);
}
}
/////////////////////////
// GraphPivotTreeWidget
/////////////////////////
GraphPivotTreeWidget::GraphPivotTreeWidget(QWidget* parent)
: PivotTreeWidget(aznew GraphPivotTreeRoot(), AZ_CRC("GraphPivotTreeId", 0xed815ba3), parent)
{
static_cast<GraphPivotTreeRoot*>(GetTreeRoot())->TraverseTree();
}
#include <Editor/View/Widgets/LoggingPanel/PivotTree/GraphPivotTree/moc_GraphPivotTree.cpp>
}
@@ -0,0 +1,179 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeCategorizer.h>
#include <Editor/View/Widgets/LoggingPanel/PivotTree/PivotTreeWidget.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#endif
namespace ScriptCanvasEditor
{
class GraphPivotTreeEntityItem
: public PivotTreeEntityItem
{
friend class EntityPivotTreeEntityItem;
public:
AZ_CLASS_ALLOCATOR(GraphPivotTreeEntityItem, AZ::SystemAllocator, 0);
AZ_RTTI(GraphPivotTreeEntityItem, "{17B2C45B-D63B-458E-9A2F-ED0A8218A77B}", PivotTreeEntityItem);
GraphPivotTreeEntityItem(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState checkState) override final;
const ScriptCanvas::GraphIdentifier& GetGraphIdentifier() const;
private:
Qt::CheckState m_checkState;
ScriptCanvas::GraphIdentifier m_graphIdentifier;
};
class GraphPivotTreeGraphItem
: public PivotTreeGraphItem
, public LoggingDataNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(GraphPivotTreeGraphItem, AZ::SystemAllocator, 0);
AZ_RTTI(GraphPivotTreeGraphItem, "{9B449D02-109E-4D9E-BA99-35C52106432C}", PivotTreeGraphItem);
GraphPivotTreeGraphItem(const AZ::Data::AssetId& assetId);
void OnDataSwitch();
void RegisterEntity(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void UnregisterEntity(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void OnChildDataChanged(GraphCanvasTreeItem* treeItem) override;
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState debugging) override final;
GraphPivotTreeEntityItem* FindDynamicallySpawnedTreeItem() const;
GraphPivotTreeEntityItem* FindEntityTreeItem(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) const;
// LoggingDataNotificationBus
void OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
////
protected:
void OnLoggingDataIdSet() override;
private:
void SetupDynamicallySpawnedElementItem(bool isChecked);
void CalculateCheckState();
Qt::CheckState m_checkState;
AZStd::unordered_multimap< AZ::EntityId, GraphPivotTreeEntityItem*> m_pivotItems;
};
class GraphPivotTreeFolder
: public PivotTreeItem
{
public:
AZ_CLASS_ALLOCATOR(GraphPivotTreeFolder, AZ::SystemAllocator, 0);
AZ_RTTI(GraphPivotTreeRoot, "{E67BBC27-6E0D-4D56-A7D4-9389FE30E909}", PivotTreeItem);
GraphPivotTreeFolder(AZStd::string_view folder);
~GraphPivotTreeFolder() override = default;
Qt::CheckState GetCheckState() const override final;
protected:
void SetCheckState(Qt::CheckState debugging) override final;
AZStd::string GetDisplayName() const override final;
void OnChildDataChanged(GraphCanvasTreeItem* treeItem) override;
private:
void CalculateCheckState();
AZStd::string m_folderName;
Qt::CheckState m_checkState;
};
class GraphPivotTreeRoot
: public PivotTreeRoot
, public LoggingDataNotificationBus::Handler
, public GraphCanvas::CategorizerInterface
, public QObject
{
public:
friend class GraphPivotTreeWidget;
AZ_CLASS_ALLOCATOR(GraphPivotTreeRoot, AZ::SystemAllocator, 0);
AZ_RTTI(GraphPivotTreeRoot, "{B4CD0FCF-F8C7-44D5-BF4D-12A52BB088CB}", PivotTreeRoot);
GraphPivotTreeRoot();
~GraphPivotTreeRoot() override = default;
// PivotTreeRoot
void OnDataSourceChanged(const LoggingDataId& aggregateDataSource) override;
////
// LoggedDataNotifications
void OnDataCaptureBegin() override;
void OnDataCaptureEnd() override;
void OnEntityGraphRegistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void OnEntityGraphUnregistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& assetId) override;
void OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
////
// Category Interface
GraphCanvas::GraphCanvasTreeItem* CreateCategoryNode(AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvasTreeItem* parent) const override;
////
protected:
// Slots to hook up into the asset model
void OnScriptCanvasGraphAssetAdded(const QModelIndex& parentIndex, int first, int last);
void OnScriptCanvasGraphAssetRemoved(const QModelIndex& parentIndex, int first, int last);
////
private:
void ProcessEntry(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
void TraverseTree(QModelIndex index = QModelIndex());
LoggingDataId m_dataSource;
AZStd::unordered_map< AZ::Data::AssetId, GraphPivotTreeGraphItem* > m_graphTreeItemMapping;
GraphCanvas::GraphCanvasTreeCategorizer m_categorizer;
LoggingDataId m_loggedDataId;
bool m_capturingData;
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_assetModel;
};
class GraphPivotTreeWidget
: public PivotTreeWidget
{
Q_OBJECT
public:
GraphPivotTreeWidget(QWidget* parent);
};
}
@@ -0,0 +1,430 @@
/*
* 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 <AzCore/Asset/AssetManagerBus.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <Editor/View/Widgets/LoggingPanel/PivotTree/PivotTreeWidget.h>
// Disable warnings in moc code
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <Editor/View/Widgets/LoggingPanel/PivotTree/ui_PivotTreeWidget.h>
AZ_POP_DISABLE_OVERRIDE_WARNING
namespace ScriptCanvasEditor
{
//////////////////
// PivotTreeItem
//////////////////
PivotTreeItem::PivotTreeItem()
: m_isPivotElement(false)
{
}
PivotTreeItem::~PivotTreeItem()
{
}
const LoggingDataId& PivotTreeItem::GetLoggingDataId() const
{
return m_loggingDataId;
}
int PivotTreeItem::GetColumnCount() const
{
return Column::Count;
}
Qt::ItemFlags PivotTreeItem::Flags([[maybe_unused]] const QModelIndex& index) const
{
Qt::ItemFlags flags = Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsUserCheckable;
if (m_isPivotElement)
{
flags |= Qt::ItemFlag::ItemIsAutoTristate;
}
return flags;
}
QVariant PivotTreeItem::Data(const QModelIndex& index, int role) const
{
switch (index.column())
{
case Column::Name:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTip)
{
return QString(GetDisplayName().c_str());
}
else if (role == Qt::CheckStateRole)
{
return GetCheckState();
}
}
break;
default:
break;
}
return QVariant();
}
bool PivotTreeItem::SetData(const QModelIndex& index, const QVariant& value, int role)
{
switch (index.column())
{
case Column::Name:
{
if (role == Qt::CheckStateRole)
{
Qt::CheckState checkState = value.value<Qt::CheckState>();
// Never want to let the user interaction set it to
if (checkState == Qt::CheckState::PartiallyChecked)
{
if (GetCheckState() == Qt::CheckState::Unchecked)
{
checkState = Qt::CheckState::Checked;
}
else
{
checkState = Qt::CheckState::Unchecked;
}
}
SetCheckState(checkState);
}
}
break;
default:
break;
}
return false;
}
void PivotTreeItem::OnChildAdded(GraphCanvasTreeItem* treeItem)
{
if (m_loggingDataId.IsValid())
{
static_cast<PivotTreeItem*>(treeItem)->SetLoggingDataId(m_loggingDataId);
}
}
void PivotTreeItem::OnLoggingDataIdSet()
{
}
void PivotTreeItem::SetLoggingDataId(const LoggingDataId& dataId)
{
if (dataId != m_loggingDataId)
{
m_loggingDataId = dataId;
for (int i = 0; i < GetChildCount(); ++i)
{
PivotTreeItem* treeItem = static_cast<PivotTreeItem*>(FindChildByRow(i));
treeItem->SetLoggingDataId(dataId);
}
OnLoggingDataIdSet();
}
}
void PivotTreeItem::SetIsPivotedElement(bool isPivotElement)
{
m_isPivotElement = isPivotElement;
}
////////////////////////
// PivotTreeEntityItem
////////////////////////
PivotTreeEntityItem::PivotTreeEntityItem(const AZ::NamedEntityId& namedEntityId)
: m_namedEntityId(namedEntityId)
{
}
const AZ::NamedEntityId& PivotTreeEntityItem::GetNamedEntityId() const
{
return m_namedEntityId;
}
AZStd::string PivotTreeEntityItem::GetDisplayName() const
{
return m_namedEntityId.ToString();
}
const AZ::EntityId& PivotTreeEntityItem::GetEntityId() const
{
return m_namedEntityId;
}
///////////////////////
// PivotTreeGraphItem
///////////////////////
PivotTreeGraphItem::PivotTreeGraphItem(const AZ::Data::AssetId& assetId)
: m_assetId(assetId)
{
// Determine the file name for our asset
AZStd::string fullPath;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(fullPath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, m_assetId);
AZStd::size_t indexOf = fullPath.find_last_of('/');
if (indexOf == AZStd::string::npos)
{
m_assetName = fullPath;
m_assetPath = "";
}
else
{
m_assetPath = fullPath.substr(0, indexOf);
m_assetName = fullPath.substr(indexOf + 1);
}
}
AZStd::string PivotTreeGraphItem::GetDisplayName() const
{
return m_assetName;
}
const AZ::Data::AssetId& PivotTreeGraphItem::GetAssetId() const
{
return m_assetId;
}
AZStd::string_view PivotTreeGraphItem::GetAssetPath() const
{
return m_assetPath;
}
//////////////////
// PivotTreeRoot
//////////////////
void PivotTreeRoot::SwitchDataSource(const LoggingDataId& aggregateDataSource)
{
SetLoggingDataId(aggregateDataSource);
OnDataSourceChanged(aggregateDataSource);
}
Qt::CheckState PivotTreeRoot::GetCheckState() const
{
return Qt::CheckState::Unchecked;
}
void PivotTreeRoot::SetCheckState(Qt::CheckState checkState)
{
AZ_UNUSED(checkState);
}
AZStd::string PivotTreeRoot::GetDisplayName() const
{
return "";
}
////////////////////////////
// PivotTreeSortProxyModel
////////////////////////////
bool PivotTreeSortProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
if (m_filter.isEmpty())
{
return true;
}
QAbstractItemModel* model = sourceModel();
QModelIndex index = model->index(sourceRow, PivotTreeItem::Column::Name, sourceParent);
PivotTreeItem* basePivotTreeItem = static_cast<PivotTreeItem*>(index.internalPointer());
QString test = model->data(index, Qt::DisplayRole).toString();
bool showRow = test.lastIndexOf(m_filterRegex) >= 0;
// Handle showing ourselves if a child is being displayed
if (!showRow && sourceModel()->hasChildren(index))
{
for (int i = 0; i < sourceModel()->rowCount(index); ++i)
{
if (filterAcceptsRow(i, index))
{
showRow = true;
break;
}
}
}
// We also want to display ourselves if any of our parents match the filter
QModelIndex parentIndex = sourceModel()->parent(index);
while (!showRow && parentIndex.isValid())
{
QString test2 = model->data(parentIndex).toString();
showRow = test2.contains(m_filterRegex);
parentIndex = sourceModel()->parent(parentIndex);
}
return showRow;
}
bool PivotTreeSortProxyModel::HasFilter() const
{
return !m_filter.isEmpty();
}
void PivotTreeSortProxyModel::SetFilter(const QString& filter)
{
m_filter = filter;
m_filterRegex = QRegExp(m_filter, Qt::CaseInsensitive);
invalidateFilter();
}
void PivotTreeSortProxyModel::ClearFilter()
{
if (HasFilter())
{
SetFilter("");
}
}
////////////////////
// PivotTreeWidget
////////////////////
PivotTreeWidget::PivotTreeWidget(PivotTreeRoot* pivotRoot, const AZ::Crc32& savingId, QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::PivotTreeWidget())
{
m_ui->setupUi(this);
m_pivotRoot = pivotRoot;
m_treeModel = aznew GraphCanvas::GraphCanvasTreeModel(pivotRoot);
m_proxyModel = aznew PivotTreeSortProxyModel();
m_proxyModel->ClearFilter();
m_proxyModel->setSourceModel(m_treeModel);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_ui->pivotTreeView->setModel(m_proxyModel);
m_ui->pivotTreeView->sortByColumn(PivotTreeItem::Column::Name, Qt::SortOrder::AscendingOrder);
m_ui->pivotTreeView->header()->setHidden(true);
m_ui->pivotTreeView->header()->setStretchLastSection(false);
m_ui->pivotTreeView->header()->setSectionResizeMode(PivotTreeItem::Column::Name, QHeaderView::ResizeMode::Stretch);
m_ui->pivotTreeView->header()->setSectionResizeMode(PivotTreeItem::Column::QT_NEEDS_A_SECOND_COLUMN_FOR_THIS_MODEL_TO_WORK_FOR_SOME_REASON, QHeaderView::ResizeMode::Fixed);
m_ui->pivotTreeView->header()->resizeSection(PivotTreeItem::Column::QT_NEEDS_A_SECOND_COLUMN_FOR_THIS_MODEL_TO_WORK_FOR_SOME_REASON, 1);
QObject::connect(m_ui->filterWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, this, &PivotTreeWidget::OnFilterChanged);
m_ui->filterWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
m_ui->pivotTreeView->InitializeTreeViewSaving(savingId);
m_ui->pivotTreeView->PauseTreeViewSaving();
QObject::connect(m_ui->pivotTreeView, &QTreeView::doubleClicked, this, &PivotTreeWidget::OnItemDoubleClicked);
}
PivotTreeWidget::~PivotTreeWidget()
{
}
void PivotTreeWidget::DisplayTree()
{
OnTreeDisplayed();
}
void PivotTreeWidget::SwitchDataSource(const LoggingDataId& aggregateDataSource)
{
{
QSignalBlocker signalBlocker(m_ui->filterWidget);
m_ui->filterWidget->ClearTextFilter();
OnFilterChanged("");
}
m_pivotRoot->SwitchDataSource(aggregateDataSource);
}
void PivotTreeWidget::OnFilterChanged(const QString& activeTextFilter)
{
bool hadFilter = m_proxyModel->HasFilter();
if (!m_proxyModel->HasFilter() && !activeTextFilter.isEmpty())
{
m_ui->pivotTreeView->UnpauseTreeViewSaving();
m_ui->pivotTreeView->CaptureTreeViewSnapshot();
m_ui->pivotTreeView->PauseTreeViewSaving();
}
m_proxyModel->SetFilter(activeTextFilter);
if (hadFilter && !m_proxyModel->HasFilter())
{
m_ui->pivotTreeView->UnpauseTreeViewSaving();
m_ui->pivotTreeView->ApplyTreeViewSnapshot();
m_ui->pivotTreeView->PauseTreeViewSaving();
}
else if (m_proxyModel->HasFilter())
{
m_ui->pivotTreeView->expandAll();
}
}
PivotTreeRoot* PivotTreeWidget::GetTreeRoot()
{
return m_pivotRoot;
}
void PivotTreeWidget::OnTreeDisplayed()
{
}
void PivotTreeWidget::OnItemDoubleClicked(const QModelIndex& modelIndex)
{
QModelIndex sourceIndex = modelIndex;
QSortFilterProxyModel* proxyModel = qobject_cast<QSortFilterProxyModel*>(m_ui->pivotTreeView->model());
if (proxyModel)
{
sourceIndex = proxyModel->mapToSource(modelIndex);
}
PivotTreeItem* pivotTreeItem = static_cast<PivotTreeItem*>(sourceIndex.internalPointer());
if (pivotTreeItem)
{
PivotTreeGraphItem* graphItem = azrtti_cast<PivotTreeGraphItem*>(pivotTreeItem);
if (graphItem)
{
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAssetId, graphItem->GetAssetId());
}
}
}
#include <Editor/View/Widgets/LoggingPanel/PivotTree/moc_PivotTreeWidget.cpp>
}
@@ -0,0 +1,208 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/PlatformDef.h>
// qbrush.h(118): warning C4251: 'QBrush::d': class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
// qwidget.h(858): warning C4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QTimer>
#include <QTreeView>
#include <QSortFilterProxyModel>
AZ_POP_DISABLE_WARNING
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeModel.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowSession.h>
#endif
namespace Ui
{
class PivotTreeWidget;
}
namespace ScriptCanvasEditor
{
class PivotTreeItem
: public GraphCanvas::GraphCanvasTreeItem
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(PivotTreeItem, "{F310C0EA-9CFE-4A8F-9CDA-46E24673B01A}", GraphCanvas::GraphCanvasTreeItem);
enum Column
{
IndexForce = -1,
Name,
// Seriously. Returning 1 causes the data to only ask for the tool tip.
// No idea why.
QT_NEEDS_A_SECOND_COLUMN_FOR_THIS_MODEL_TO_WORK_FOR_SOME_REASON,
Count
};
PivotTreeItem();
~PivotTreeItem();
const LoggingDataId& GetLoggingDataId() const;
// GraphCanvasTreeItem
int GetColumnCount() const override final;
Qt::ItemFlags Flags(const QModelIndex& index) const override final;
QVariant Data(const QModelIndex& index, int role) const override final;
bool SetData(const QModelIndex& index, const QVariant& value, int role) override final;
void OnChildAdded(GraphCanvasTreeItem* treeItem) override final;
////
virtual Qt::CheckState GetCheckState() const = 0;
virtual void SetCheckState(Qt::CheckState checkState) = 0;
protected:
virtual AZStd::string GetDisplayName() const = 0;
virtual void OnLoggingDataIdSet();
void SetLoggingDataId(const LoggingDataId& dataId);
void SetIsPivotedElement(bool isPivotedElement);
private:
bool m_isPivotElement;
LoggingDataId m_loggingDataId;
};
class PivotTreeEntityItem
: public PivotTreeItem
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeEntityItem, AZ::SystemAllocator, 0);
AZ_RTTI(PivotTreeEntityItem, "{67725865-7004-441D-84BB-D38FF491A3FD}", PivotTreeItem);
PivotTreeEntityItem(const AZ::NamedEntityId& entityId);
const AZ::NamedEntityId& GetNamedEntityId() const;
protected:
AZStd::string GetDisplayName() const override final;
const AZ::EntityId& GetEntityId() const;
private:
AZ::NamedEntityId m_namedEntityId;
};
class PivotTreeGraphItem
: public PivotTreeItem
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeGraphItem, AZ::SystemAllocator, 0);
AZ_RTTI(PivotTreeGraphItem, "{37FCAC77-DE32-4B1B-97FD-66852EC31CAB}", PivotTreeItem);
PivotTreeGraphItem(const AZ::Data::AssetId& assetId);
const AZ::Data::AssetId& GetAssetId() const;
protected:
AZStd::string GetDisplayName() const override final;
AZStd::string_view GetAssetPath() const;
private:
AZ::Data::AssetId m_assetId;
AZStd::string m_assetPath;
AZStd::string m_assetName;
};
class PivotTreeRoot
: public PivotTreeItem
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeRoot, AZ::SystemAllocator, 0);
AZ_RTTI(PivotTreeRoot, "{E172AB89-49BA-429F-AC83-9CCBD6A3B1B9}", PivotTreeItem);
PivotTreeRoot() = default;
void SwitchDataSource(const LoggingDataId& aggregateDataSource);
protected:
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState checkState) override final;
AZStd::string GetDisplayName() const override final;
virtual void OnDataSourceChanged(const LoggingDataId& aggregateDataSource) = 0;
private:
LoggingDataId m_loggingDataId;
};
class PivotTreeSortProxyModel
: public QSortFilterProxyModel
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeSortProxyModel, AZ::SystemAllocator, 0);
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
bool HasFilter() const;
void SetFilter(const QString& filter);
void ClearFilter();
private:
QString m_filter;
QRegExp m_filterRegex;
};
class PivotTreeWidget
: public QWidget
{
Q_OBJECT
public:
~PivotTreeWidget();
void DisplayTree();
void SwitchDataSource(const LoggingDataId& aggregateDataSource);
public Q_SLOT:
void OnFilterChanged(const QString& activeTextFilter);
protected:
PivotTreeWidget(PivotTreeRoot* pivotRoot, const AZ::Crc32& savingId, QWidget* parent);
PivotTreeRoot* GetTreeRoot();
virtual void OnTreeDisplayed();
private:
void OnItemDoubleClicked(const QModelIndex& modelIndex);
AZStd::unique_ptr<Ui::PivotTreeWidget> m_ui;
PivotTreeRoot* m_pivotRoot;
GraphCanvas::GraphCanvasTreeModel* m_treeModel;
PivotTreeSortProxyModel* m_proxyModel;
};
}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PivotTreeWidget</class>
<widget class="QWidget" name="PivotTreeWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>372</width>
<height>476</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="filterWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="AzToolsFramework::QTreeViewWithStateSaving" name="pivotTreeView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QWidget</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzToolsFramework::QTreeViewWithStateSaving</class>
<extends>QTreeView</extends>
<header location="global">AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,384 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoggingWindow</class>
<widget class="QDockWidget" name="LoggingWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>950</width>
<height>375</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="windowTitle">
<string>Script Canvas Log</string>
</property>
<widget class="QWidget" name="center">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>950</width>
<height>353</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="entityPivotButton">
<property name="text">
<string>Entities</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="graphPivotButton">
<property name="text">
<string>Graphs</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="entitiesPage">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="ScriptCanvasEditor::EntityPivotTreeWidget" name="entitiesPivotTreeWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="canvasPage">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="canvasPivotTreeWidget" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>4</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame_4">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="AzToolsFramework::TargetSelectorButton" name="pushButton_3">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Target: None</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="openLog">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="AzQtComponents::TabWidget" name="tabWidget">
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="tabsClosable">
<bool>true</bool>
</property>
<property name="movable">
<bool>false</bool>
</property>
<widget class="QWidget" name="emptyCapture">
<attribute name="title">
<string>Live Capture</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>AzToolsFramework::TargetSelectorButton</class>
<extends>QPushButton</extends>
<header>AzToolsFramework/UI/UICore/TargetSelectorButton.hxx</header>
</customwidget>
<customwidget>
<class>ScriptCanvasEditor::EntityPivotTreeWidget</class>
<extends>QWidget</extends>
<header>Editor/View/Widgets/LoggingPanel/PivotTree/EntityPivotTree/EntityPivotTree.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::TabWidget</class>
<extends>QTabWidget</extends>
<header location="global">AzQtComponents/Components/Widgets/TabWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoggingWindowSession</class>
<widget class="QWidget" name="LoggingWindowSession">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>521</width>
<height>352</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="filterWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="garbageIcon">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="optionsIcon">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTableView" name="logTable"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QWidget</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,46 @@
/*
* 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 <Editor/View/Widgets/MainWindowStatusWidget.h>
#include <Editor/View/Widgets/ui_MainWindowStatusWidget.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
namespace ScriptCanvasEditor
{
///////////////////////////
// MainWindowStatusWidget
///////////////////////////
MainWindowStatusWidget::MainWindowStatusWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::MainWindowStatusWidget())
{
m_ui->setupUi(this);
QObject::connect(m_ui->showErrorButton, &QToolButton::clicked, this, &MainWindowStatusWidget::OnErrorButtonPressed);
QObject::connect(m_ui->showWarningButton, &QToolButton::clicked, this, &MainWindowStatusWidget::OnWarningButtonPressed);
GraphValidatorDockWidgetNotificationBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
OnResultsChanged(0, 0);
}
void MainWindowStatusWidget::OnResultsChanged(int errorCount, int warningCount)
{
m_ui->showErrorButton->setText(QString("%1 Errors").arg(errorCount));
m_ui->showWarningButton->setText(QString("%1 Warnings").arg(warningCount));
}
#include <Editor/View/Widgets/moc_MainWindowStatusWidget.cpp>
}
@@ -0,0 +1,55 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/Memory/SystemAllocator.h>
#include <GraphCanvas/Editor/AssetEditorBus.h>
#include <Editor/View/Widgets/ValidationPanel/GraphValidationDockWidgetBus.h>
#endif
namespace Ui
{
class MainWindowStatusWidget;
}
namespace ScriptCanvasEditor
{
class MainWindowStatusWidget
: public QWidget
, public GraphValidatorDockWidgetNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(MainWindowStatusWidget, AZ::SystemAllocator, 0);
MainWindowStatusWidget(QWidget* parent = nullptr);
~MainWindowStatusWidget() = default;
// GraphValidatorDockWidgetNotificationBus
void OnResultsChanged(int errorCount, int warningCount) override;
////
public slots:
signals:
void OnErrorButtonPressed();
void OnWarningButtonPressed();
private:
AZStd::unique_ptr<Ui::MainWindowStatusWidget> m_ui;
};
}
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindowStatusWidget</class>
<widget class="QWidget" name="MainWindowStatusWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>530</width>
<height>34</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="showErrorButton">
<property name="text">
<string>0 Errors</string>
</property>
<property name="icon">
<iconset resource="../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/error_icon.png</normaloff>:/ScriptCanvasEditorResources/Resources/error_icon.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="showWarningButton">
<property name="text">
<string>0 Warnings</string>
</property>
<property name="icon">
<iconset resource="../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/warning_symbol.png</normaloff>:/ScriptCanvasEditorResources/Resources/warning_symbol.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../Windows/ScriptCanvasEditorResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,141 @@
/*
* 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 <AzCore/Serialization/SerializeContext.h>
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include "CreateNodeMimeEvent.h"
#include "ScriptCanvas/Bus/RequestBus.h"
namespace ScriptCanvasEditor
{
////////////////////////
// CreateNodeMimeEvent
////////////////////////
void CreateNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateNodeMimeEvent, GraphCanvas::CreateSplicingNodeMimeEvent>()
->Version(0)
;
}
}
const ScriptCanvasEditor::NodeIdPair& CreateNodeMimeEvent::GetCreatedPair() const
{
return m_nodeIdPair;
}
bool CreateNodeMimeEvent::ExecuteEvent(const AZ::Vector2&, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
if (!scriptCanvasId.IsValid() || !graphCanvasGraphId.IsValid())
{
return false;
}
m_nodeIdPair = CreateNode(scriptCanvasId);
if (m_nodeIdPair.m_graphCanvasId.IsValid() && m_nodeIdPair.m_scriptCanvasId.IsValid())
{
m_createdNodeId = m_nodeIdPair.m_graphCanvasId;
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, m_nodeIdPair.m_graphCanvasId, sceneDropPosition);
GraphCanvas::SceneMemberUIRequestBus::Event(m_nodeIdPair.m_graphCanvasId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
ScriptCanvasEditor::NodeCreationNotificationBus::Event(scriptCanvasId, &ScriptCanvasEditor::NodeCreationNotifications::OnGraphCanvasNodeCreated, m_nodeIdPair.m_graphCanvasId);
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
AZ_Assert(gridId.IsValid(), "Grid must be valid, graphCanvasGraphId is likely incorrect");
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
sceneDropPosition += offset;
return true;
}
else
{
if (m_nodeIdPair.m_graphCanvasId.IsValid())
{
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::DeleteEntity, m_nodeIdPair.m_graphCanvasId);
}
if (m_nodeIdPair.m_scriptCanvasId.IsValid())
{
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::DeleteEntity, m_nodeIdPair.m_scriptCanvasId);
}
}
return false;
}
AZ::EntityId CreateNodeMimeEvent::CreateSplicingNode(const AZ::EntityId& graphCanvasGraphId)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
ScriptCanvasEditor::NodeIdPair idPair = CreateNode(scriptCanvasId);
if (idPair.m_graphCanvasId.IsValid() && idPair.m_scriptCanvasId.IsValid())
{
return idPair.m_graphCanvasId;
}
return AZ::EntityId();
}
///////////////////////////////////
// SpecializedCreateNodeMimeEvent
///////////////////////////////////
void SpecializedCreateNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<SpecializedCreateNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
;
}
}
/////////////////////////////
// MultiCreateNodeMimeEvent
/////////////////////////////
void MultiCreateNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<MultiCreateNodeMimeEvent, SpecializedCreateNodeMimeEvent>()
->Version(0)
;
}
}
}
@@ -0,0 +1,79 @@
/*
* 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 <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <GraphCanvas/Widgets/MimeEvents/CreateSplicingNodeMimeEvent.h>
#include "ScriptCanvas/Bus/NodeIdPair.h"
#include <ScriptCanvas/Core/Core.h>
namespace ScriptCanvasEditor
{
class CreateNodeMimeEvent
: public GraphCanvas::CreateSplicingNodeMimeEvent
{
public:
AZ_RTTI(CreateNodeMimeEvent, "{95C84213-1FF8-42FF-96F3-37B80B7E2C20}", GraphCanvas::CreateSplicingNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateNodeMimeEvent() = default;
~CreateNodeMimeEvent() = default;
const ScriptCanvasEditor::NodeIdPair& GetCreatedPair() const;
bool ExecuteEvent(const AZ::Vector2& mouseDropPosition, AZ::Vector2& dropPosition, const AZ::EntityId& graphCanvasGraphId) override final;
AZ::EntityId CreateSplicingNode(const AZ::EntityId& graphCanvasGraphId) override;
protected:
virtual ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const = 0;
NodeIdPair m_nodeIdPair;
};
// There are a couple of cases where we have some weird construction steps that aren't captured in the CreateNodeMimeEvent
// To deal with those cases, we want to make a specialized mime event so we can catch these cases from the context menu
// and execute the right functions.
class SpecializedCreateNodeMimeEvent
: public GraphCanvas::GraphCanvasMimeEvent
{
public:
AZ_RTTI(SpecializedCreateNodeMimeEvent, "{7909C855-B6DA-47E4-97DB-BBC8315C30B1}", GraphCanvas::GraphCanvasMimeEvent);
AZ_CLASS_ALLOCATOR(SpecializedCreateNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
SpecializedCreateNodeMimeEvent() = default;
~SpecializedCreateNodeMimeEvent() = default;
virtual NodeIdPair ConstructNode(const AZ::EntityId& scriptCanvasGraphId, const AZ::Vector2& scenePosition) = 0;
};
// Special case specialization here for some automation procedures.
// Want to be able to generate all of the possible events from a MultiCreationNode and handle
// them all in an automated way
class MultiCreateNodeMimeEvent
: public SpecializedCreateNodeMimeEvent
{
public:
AZ_RTTI(MultiCreateNodeMimeEvent, "{44A3F43F-E6D3-4EC7-8E80-82981661603E}", SpecializedCreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(MultiCreateNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
virtual AZStd::vector< GraphCanvas::GraphCanvasMimeEvent* > CreateMimeEvents() const = 0;
};
}
@@ -0,0 +1,331 @@
/*
* 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 <AzCore/Serialization/SerializeContext.h>
#include <QCoreApplication>
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include "EBusNodePaletteTreeItemTypes.h"
#include "Editor/Components/IconComponent.h"
#include "Editor/Nodes/NodeUtils.h"
#include "Editor/Translation/TranslationHelper.h"
#include "ScriptCanvas/Bus/RequestBus.h"
#include "Editor/Include/ScriptCanvas/GraphCanvas/NodeDescriptorBus.h"
#include "Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h"
#include <Core/Attributes.h>
#include <Libraries/Core/EBusEventHandler.h>
#include <Libraries/Core/Method.h>
namespace ScriptCanvasEditor
{
//////////////////////////////
// CreateEBusSenderMimeEvent
//////////////////////////////
void CreateEBusSenderMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateEBusSenderMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
->Field("BusName", &CreateEBusSenderMimeEvent::m_busName)
->Field("EventName", &CreateEBusSenderMimeEvent::m_eventName)
;
}
}
CreateEBusSenderMimeEvent::CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName)
: m_busName(busName.data())
, m_eventName(eventName.data())
{
}
ScriptCanvasEditor::NodeIdPair CreateEBusSenderMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
return Nodes::CreateObjectMethodNode(m_busName, m_eventName, scriptCanvasId);
}
/////////////////////////////////
// EBusSendEventPaletteTreeItem
/////////////////////////////////
const QString& EBusSendEventPaletteTreeItem::GetDefaultIcon()
{
static QString defaultIcon;
if (defaultIcon.isEmpty())
{
defaultIcon = IconComponent::LookupClassIcon(ScriptCanvas::Nodes::Core::EBusEventHandler::RTTI_Type()).c_str();
}
return defaultIcon;
}
EBusSendEventPaletteTreeItem::EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busIdentifier, const ScriptCanvas::EBusEventId& eventIdentifier)
: DraggableNodePaletteTreeItem(eventName, ScriptCanvasEditor::AssetEditorId)
, m_busName(busName.data())
, m_eventName(eventName.data())
, m_busId(busIdentifier)
, m_eventId(eventIdentifier)
{
AZStd::string displayEventName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusSender, m_busName.toUtf8().data(), m_eventName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Name);
if (displayEventName.empty())
{
SetName(m_eventName);
}
else
{
SetName(displayEventName.c_str());
}
AZStd::string displayEventTooltip = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusSender, m_busName.toUtf8().data(), m_eventName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Tooltip);
if (!displayEventTooltip.empty())
{
SetToolTip(displayEventTooltip.c_str());
}
SetTitlePalette("MethodNodeTitlePalette");
}
GraphCanvas::GraphCanvasMimeEvent* EBusSendEventPaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateEBusSenderMimeEvent(m_busName.toUtf8().data(), m_eventName.toUtf8().data());
}
AZStd::string EBusSendEventPaletteTreeItem::GetBusName() const
{
return m_busName.toUtf8().data();
}
AZStd::string EBusSendEventPaletteTreeItem::GetEventName() const
{
return m_eventName.toUtf8().data();
}
ScriptCanvas::EBusBusId EBusSendEventPaletteTreeItem::GetBusId() const
{
return m_busId;
}
ScriptCanvas::EBusEventId EBusSendEventPaletteTreeItem::GetEventId() const
{
return m_eventId;
}
///////////////////////////////
// CreateEBusHandlerMimeEvent
///////////////////////////////
void CreateEBusHandlerMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateEBusHandlerMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
->Field("BusName", &CreateEBusHandlerMimeEvent::m_busName)
;
}
}
CreateEBusHandlerMimeEvent::CreateEBusHandlerMimeEvent(AZStd::string_view busName)
: m_busName(busName.data())
{
}
ScriptCanvasEditor::NodeIdPair CreateEBusHandlerMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
return Nodes::CreateEbusWrapperNode(m_busName, scriptCanvasId);
}
////////////////////////////////////
// CreateEBusHandlerEventMimeEvent
////////////////////////////////////
void CreateEBusHandlerEventMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateEBusHandlerEventMimeEvent, SpecializedCreateNodeMimeEvent>()
->Version(0)
->Field("BusName", &CreateEBusHandlerEventMimeEvent::m_busName)
->Field("EventName", &CreateEBusHandlerEventMimeEvent::m_eventName)
->Field("EventId", &CreateEBusHandlerEventMimeEvent::m_eventId)
;
}
}
CreateEBusHandlerEventMimeEvent::CreateEBusHandlerEventMimeEvent(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusEventId& eventId)
: m_busName(busName)
, m_eventName(eventName)
, m_eventId(eventId)
{
}
NodeIdPair CreateEBusHandlerEventMimeEvent::ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition)
{
NodeIdPair eventNode = CreateEventNode(graphCanvasGraphId, scenePosition);
CreateEBusHandlerMimeEvent ebusMimeEvent(m_busName);
AZ::Vector2 position = scenePosition;
if (ebusMimeEvent.ExecuteEvent(position, position, graphCanvasGraphId))
{
NodeIdPair handlerNode = ebusMimeEvent.GetCreatedPair();
GraphCanvas::WrappedNodeConfiguration configuration;
EBusHandlerNodeDescriptorRequestBus::EventResult(configuration, handlerNode.m_graphCanvasId, &EBusHandlerNodeDescriptorRequests::GetEventConfiguration, m_eventId);
GraphCanvas::WrapperNodeRequestBus::Event(handlerNode.m_graphCanvasId, &GraphCanvas::WrapperNodeRequests::WrapNode, eventNode.m_graphCanvasId, configuration);
}
return eventNode;
}
bool CreateEBusHandlerEventMimeEvent::ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
return ExecuteEventImpl(mousePosition, sceneDropPosition, graphCanvasGraphId).m_graphCanvasId.IsValid();
}
NodeIdPair CreateEBusHandlerEventMimeEvent::CreateEventNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) const
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
NodeIdPair nodeIdPair;
nodeIdPair.m_graphCanvasId = Nodes::DisplayEbusEventNode(graphCanvasGraphId, m_busName, m_eventName, m_eventId);
if (nodeIdPair.m_graphCanvasId.IsValid())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, nodeIdPair.m_graphCanvasId, scenePosition);
}
return nodeIdPair;
}
NodeIdPair CreateEBusHandlerEventMimeEvent::ExecuteEventImpl([[maybe_unused]] const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
NodeIdPair eventNode = CreateEventNode(graphCanvasGraphId, sceneDropPosition);
if (eventNode.m_graphCanvasId.IsValid())
{
GraphCanvas::SceneMemberUIRequestBus::Event(eventNode.m_graphCanvasId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
ScriptCanvasEditor::NodeCreationNotificationBus::Event(scriptCanvasId, &ScriptCanvasEditor::NodeCreationNotifications::OnGraphCanvasNodeCreated, eventNode.m_graphCanvasId);
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
sceneDropPosition += offset;
}
return eventNode;
}
void CreateEBusHandlerEventMimeEvent::ConfigureEvent(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusEventId& eventId)
{
m_busName = busName;
m_eventName = eventName;
m_eventId = eventId;
}
///////////////////////////////////
// EBusHandleEventPaletteTreeItem
///////////////////////////////////
const QString& EBusHandleEventPaletteTreeItem::GetDefaultIcon()
{
static QString defaultIcon;
if (defaultIcon.isEmpty())
{
defaultIcon = IconComponent::LookupClassIcon(ScriptCanvas::Nodes::Core::Method::RTTI_Type()).c_str();
}
return defaultIcon;
}
EBusHandleEventPaletteTreeItem::EBusHandleEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId)
: DraggableNodePaletteTreeItem(eventName, ScriptCanvasEditor::AssetEditorId)
, m_busName(busName)
, m_eventName(eventName)
, m_busId(busId)
, m_eventId(eventId)
{
AZStd::string displayEventName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusHandler, m_busName.c_str(), m_eventName.c_str(), TranslationItemType::Node, TranslationKeyId::Name);
if (displayEventName.empty())
{
SetName(m_eventName.c_str());
}
else
{
SetName(displayEventName.c_str());
}
AZStd::string displayEventTooltip = TranslationHelper::GetKeyTranslation(TranslationContextGroup::EbusHandler, m_busName.c_str(), m_eventName.c_str(), TranslationItemType::Node, TranslationKeyId::Tooltip);
if (!displayEventTooltip.empty())
{
SetToolTip(displayEventTooltip.c_str());
}
SetTitlePalette("HandlerNodeTitlePalette");
}
GraphCanvas::GraphCanvasMimeEvent* EBusHandleEventPaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateEBusHandlerEventMimeEvent(m_busName, m_eventName, m_eventId);
}
AZStd::string EBusHandleEventPaletteTreeItem::GetBusName() const
{
return m_busName;
}
AZStd::string EBusHandleEventPaletteTreeItem::GetEventName() const
{
return m_eventName;
}
ScriptCanvas::EBusBusId EBusHandleEventPaletteTreeItem::GetBusId() const
{
return m_busId;
}
ScriptCanvas::EBusEventId EBusHandleEventPaletteTreeItem::GetEventId() const
{
return m_eventId;
}
}
@@ -0,0 +1,164 @@
/*
* 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 <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include "CreateNodeMimeEvent.h"
#include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
namespace ScriptCanvasEditor
{
// <EbusSender>
class CreateEBusSenderMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateEBusSenderMimeEvent, "{7EFA0742-BBF6-45FD-B378-C73577DEE464}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateEBusSenderMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateEBusSenderMimeEvent() = default;
CreateEBusSenderMimeEvent(AZStd::string_view busName, AZStd::string_view eventName);
~CreateEBusSenderMimeEvent() = default;
protected:
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const override;
private:
AZStd::string m_busName;
AZStd::string m_eventName;
};
class EBusSendEventPaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
private:
static const QString& GetDefaultIcon();
public:
AZ_CLASS_ALLOCATOR(EBusSendEventPaletteTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(EBusSendEventPaletteTreeItem, "{26258B0A-8E2C-434D-ACAD-3DE85E64A4F8}", GraphCanvas::DraggableNodePaletteTreeItem);
EBusSendEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventIdentifier);
~EBusSendEventPaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
AZStd::string GetBusName() const;
AZStd::string GetEventName() const;
ScriptCanvas::EBusBusId GetBusId() const;
ScriptCanvas::EBusEventId GetEventId() const;
private:
QString m_busName;
QString m_eventName;
ScriptCanvas::EBusBusId m_busId;
ScriptCanvas::EBusEventId m_eventId;
};
// </EbusSender>
// <EbusHandler>
class CreateEBusHandlerMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateEBusHandlerMimeEvent, "{7B205AA9-2A27-4508-9277-FB8D5C6BE5BC}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateEBusHandlerMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateEBusHandlerMimeEvent() = default;
CreateEBusHandlerMimeEvent(AZStd::string_view busName);
~CreateEBusHandlerMimeEvent() = default;
protected:
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const override;
private:
AZStd::string m_busName;
};
// </EbusHandler>
// <EbusHandlerEvent>
class CreateEBusHandlerEventMimeEvent
: public SpecializedCreateNodeMimeEvent
{
public:
AZ_RTTI(CreateEBusHandlerEventMimeEvent, "{0F5FAB1D-7E84-44E6-8161-630576490249}", SpecializedCreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateEBusHandlerEventMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateEBusHandlerEventMimeEvent() = default;
CreateEBusHandlerEventMimeEvent(AZStd::string_view busName, AZStd::string_view methodName, const ScriptCanvas::EBusEventId& eventId);
~CreateEBusHandlerEventMimeEvent() = default;
AZStd::string_view GetBusName() { return m_busName; }
AZStd::string_view GetEventName() { return m_eventName; }
ScriptCanvas::EBusEventId GetEventId() { return m_eventId; }
NodeIdPair ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) override;
bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId) override;
NodeIdPair CreateEventNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) const;
protected:
NodeIdPair ExecuteEventImpl(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId);
void ConfigureEvent(AZStd::string_view busNane, AZStd::string_view eventName, const ScriptCanvas::EBusEventId& eventId);
private:
AZStd::string m_busName;
AZStd::string m_eventName;
ScriptCanvas::EBusEventId m_eventId;
};
// These nodes will create a purely visual representation of the data. They do not have a corresponding ScriptCanvas node, but instead
// share slots from the owning EBus Handler node. This creates a bit of weirdness with the general creation, since we no longer have a 1:1
// and need to create a bus wrapper for these things whenever we try to make them.
class EBusHandleEventPaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
private:
static const QString& GetDefaultIcon();
public:
AZ_CLASS_ALLOCATOR(EBusHandleEventPaletteTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(EBusHandleEventPaletteTreeItem, "{99A95EC0-1DF8-45B8-8229-D6D12E32CBED}", GraphCanvas::DraggableNodePaletteTreeItem);
EBusHandleEventPaletteTreeItem(AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId);
~EBusHandleEventPaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
AZStd::string GetBusName() const;
AZStd::string GetEventName() const;
ScriptCanvas::EBusBusId GetBusId() const;
ScriptCanvas::EBusEventId GetEventId() const;
private:
AZStd::string m_busName;
AZStd::string m_eventName;
ScriptCanvas::EBusBusId m_busId;
ScriptCanvas::EBusEventId m_eventId;
};
// </EbusHandlerEvent>
}
@@ -0,0 +1,143 @@
/*
* 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 <qmenu.h>
#include <QPixmap>
#include <AzToolsFramework/AssetEditor/AssetEditorUtils.h>
#include <Editor/Nodes/NodeUtils.h>
#include <ScriptCanvas/Bus/EditorScriptCanvasBus.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/Components/Nodes/Wrapper/WrapperNodeBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/View/Widgets/NodePalette/FunctionNodePaletteTreeItemTypes.h>
namespace ScriptCanvasEditor
{
//////////////////////////////////////////
// CreateFunctionMimeEvent
//////////////////////////////////////////
void CreateFunctionMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
{
serializeContext->Class<CreateFunctionMimeEvent, CreateNodeMimeEvent>()
->Version(4)
->Field("AssetId", &CreateFunctionMimeEvent::m_assetId)
;
}
}
CreateFunctionMimeEvent::CreateFunctionMimeEvent(const AZ::Data::AssetId& assetId)
: m_assetId(assetId)
{
}
bool CreateFunctionMimeEvent::CanGraphHandleEvent(const GraphCanvas::GraphId& graphId) const
{
AZ::EntityId scGraphId;
GeneralRequestBus::BroadcastResult(scGraphId, &GeneralRequests::GetScriptCanvasId, graphId);
bool isFunctionGraph = false;
EditorGraphRequestBus::EventResult(isFunctionGraph, scGraphId, &EditorGraphRequests::IsFunctionGraph);
return !isFunctionGraph;
}
ScriptCanvasEditor::NodeIdPair CreateFunctionMimeEvent::CreateNode(const AZ::EntityId& scriptCanvasGraphId) const
{
return Nodes::CreateFunctionNode(scriptCanvasGraphId, m_assetId);
}
/////////////////////////////////////
// FunctionPaletteTreeItem
/////////////////////////////////////
FunctionPaletteTreeItem::FunctionPaletteTreeItem(const char* name, const AZ::Data::AssetId& sourceAssetId, const AZ::Data::AssetId& runtimeAssetId)
: GraphCanvas::DraggableNodePaletteTreeItem(name, ScriptCanvasEditor::AssetEditorId)
, m_editIcon(":/ScriptCanvasEditorResources/Resources/edit_icon.png")
, m_sourceAssetId(sourceAssetId)
, m_runtimeAssetId(runtimeAssetId)
{
//TODO
//SetToolTip(m_methodDefinition.GetTooltip().c_str());
SetTitlePalette("FunctionNodeTitlePalette");
}
GraphCanvas::GraphCanvasMimeEvent* FunctionPaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateFunctionMimeEvent(m_runtimeAssetId);
}
QVariant FunctionPaletteTreeItem::OnData(const QModelIndex& index, int role) const
{
if (index.column() == NodePaletteTreeItem::Column::Customization)
{
if (IsHovered())
{
if (role == Qt::DecorationRole)
{
return m_editIcon;
}
else if (role == Qt::ToolTipRole)
{
return QString("Opens the Script Event Editor to edit the Script Function - %1.").arg(GetName().toStdString().c_str());
}
}
}
return GraphCanvas::DraggableNodePaletteTreeItem::OnData(index, role);
}
const AZ::Data::AssetId& FunctionPaletteTreeItem::GetSourceAssetId() const
{
return m_sourceAssetId;
}
void FunctionPaletteTreeItem::OnHoverStateChanged()
{
SignalDataChanged();
}
void FunctionPaletteTreeItem::OnClicked(int row)
{
if (row == NodePaletteTreeItem::Column::Customization)
{
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAsset, m_sourceAssetId, -1);
}
}
bool FunctionPaletteTreeItem::OnDoubleClicked(int row)
{
if (row != NodePaletteTreeItem::Column::Customization)
{
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAsset, m_sourceAssetId, -1);
return true;
}
return false;
}
}
@@ -0,0 +1,88 @@
/*
* 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 <QIcon>
#include <AzCore/Asset/AssetCommon.h>
#include <GraphCanvas/Widgets/GraphCanvasMimeEvent.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include <ScriptCanvas/Bus/NodeIdPair.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/Asset/Functions/ScriptCanvasFunctionAsset.h>
#include <Editor/View/Widgets/NodePalette/CreateNodeMimeEvent.h>
#include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
namespace AZ
{
class ReflectContext;
}
namespace ScriptCanvasEditor
{
class CreateFunctionMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateFunctionMimeEvent, "{BCB4226C-4863-4646-838C-45ABD662C9BB}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateFunctionMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateFunctionMimeEvent() = default;
CreateFunctionMimeEvent(const AZ::Data::AssetId& assetId);
~CreateFunctionMimeEvent() = default;
bool CanGraphHandleEvent(const GraphCanvas::GraphId& graphId) const override;
ScriptCanvasEditor::NodeIdPair CreateNode(const AZ::EntityId& graphId) const override;
private:
AZ::Data::AssetId m_assetId;
};
class FunctionPaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_RTTI(FunctionPaletteTreeItem, "{AF75BBAD-BC8A-46D2-81B6-54C0E6CB3E41}", GraphCanvas::DraggableNodePaletteTreeItem);
AZ_CLASS_ALLOCATOR(FunctionPaletteTreeItem, AZ::SystemAllocator, 0);
FunctionPaletteTreeItem(const char* name, const AZ::Data::AssetId& sourceAssetId, const AZ::Data::AssetId& runtimeAssetId);
~FunctionPaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const;
QVariant OnData(const QModelIndex& index, int role) const;
const AZ::Data::AssetId& GetSourceAssetId() const;
protected:
void OnHoverStateChanged() override;
void OnClicked(int row) override;
bool OnDoubleClicked(int row) override;
private:
QIcon m_editIcon;
AZ::Data::AssetId m_sourceAssetId;
AZ::Data::AssetId m_runtimeAssetId;
};
}
@@ -0,0 +1,170 @@
/*
* 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 <AzCore/Serialization/SerializeContext.h>
#include <QCoreApplication>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/Nodes/NodeUIBus.h>
#include "GeneralNodePaletteTreeItemTypes.h"
#include "Editor/Components/IconComponent.h"
#include "Editor/Nodes/NodeUtils.h"
#include "Editor/Translation/TranslationHelper.h"
#include "ScriptCanvas/Bus/RequestBus.h"
#include "Editor/Include/ScriptCanvas/GraphCanvas/NodeDescriptorBus.h"
#include "Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h"
#include <Core/Attributes.h>
#include <Libraries/Core/Method.h>
namespace ScriptCanvasEditor
{
///////////////////////////////
// CreateClassMethodMimeEvent
///////////////////////////////
void CreateClassMethodMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateClassMethodMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
->Field("ClassName", &CreateClassMethodMimeEvent::m_className)
->Field("MethodName", &CreateClassMethodMimeEvent::m_methodName)
;
}
}
CreateClassMethodMimeEvent::CreateClassMethodMimeEvent(const QString& className, const QString& methodName)
: m_className(className.toUtf8().data())
, m_methodName(methodName.toUtf8().data())
{
}
ScriptCanvasEditor::NodeIdPair CreateClassMethodMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
return Nodes::CreateObjectMethodNode(m_className, m_methodName, scriptCanvasId);
}
////////////////////////////////////
// ClassMethodEventPaletteTreeItem
////////////////////////////////////
ClassMethodEventPaletteTreeItem::ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName)
: DraggableNodePaletteTreeItem(methodName, ScriptCanvasEditor::AssetEditorId)
, m_className(className.data())
, m_methodName(methodName.data())
{
AZStd::string displayMethodName = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, m_className.toUtf8().data(), m_methodName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Name);
if (displayMethodName.empty())
{
SetName(m_methodName);
}
else
{
SetName(displayMethodName.c_str());
}
AZStd::string displayEventTooltip = TranslationHelper::GetKeyTranslation(TranslationContextGroup::ClassMethod, m_className.toUtf8().data(), m_methodName.toUtf8().data(), TranslationItemType::Node, TranslationKeyId::Tooltip);
if (!displayEventTooltip.empty())
{
SetToolTip(displayEventTooltip.c_str());
}
SetTitlePalette("MethodNodeTitlePalette");
}
GraphCanvas::GraphCanvasMimeEvent* ClassMethodEventPaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateClassMethodMimeEvent(m_className, m_methodName);
}
AZStd::string ClassMethodEventPaletteTreeItem::GetClassMethodName() const
{
return m_className.toUtf8().data();
}
AZStd::string ClassMethodEventPaletteTreeItem::GetMethodName() const
{
return m_methodName.toUtf8().data();
}
//////////////////////////////
// CreateCustomNodeMimeEvent
//////////////////////////////
void CreateCustomNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateCustomNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(2)
->Field("TypeId", &CreateCustomNodeMimeEvent::m_typeId)
->Field("StyleOverride", &CreateCustomNodeMimeEvent::m_styleOverride)
->Field("TitlePalette", &CreateCustomNodeMimeEvent::m_titlePalette)
;
}
}
CreateCustomNodeMimeEvent::CreateCustomNodeMimeEvent(const AZ::Uuid& typeId)
: m_typeId(typeId)
{
}
CreateCustomNodeMimeEvent::CreateCustomNodeMimeEvent(const AZ::Uuid& typeId, const AZStd::string& styleOverride, const AZStd::string& titlePalette)
: m_typeId(typeId)
, m_styleOverride(styleOverride)
, m_titlePalette(titlePalette)
{
}
ScriptCanvasEditor::NodeIdPair CreateCustomNodeMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
Nodes::StyleConfiguration styleConfiguration;
styleConfiguration.m_nodeSubStyle = m_styleOverride;
styleConfiguration.m_titlePalette = m_titlePalette;
return Nodes::CreateNode(m_typeId, scriptCanvasId, styleConfiguration);
}
//////////////////////////////
// CustomNodePaletteTreeItem
//////////////////////////////
CustomNodePaletteTreeItem::CustomNodePaletteTreeItem(const AZ::Uuid& typeId, AZStd::string_view nodeName)
: DraggableNodePaletteTreeItem(nodeName, ScriptCanvasEditor::AssetEditorId)
, m_typeId(typeId)
{
}
GraphCanvas::GraphCanvasMimeEvent* CustomNodePaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateCustomNodeMimeEvent(m_typeId, GetStyleOverride(), GetTitlePalette());
}
AZ::Uuid CustomNodePaletteTreeItem::GetTypeId() const
{
return m_typeId;
}
}
@@ -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
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include "CreateNodeMimeEvent.h"
namespace ScriptCanvasEditor
{
// <ClassMethod>
class CreateClassMethodMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateClassMethodMimeEvent, "{20641353-0513-4399-97D4-5509377BF0C8}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateClassMethodMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateClassMethodMimeEvent() = default;
CreateClassMethodMimeEvent(const QString& className, const QString& methodName);
~CreateClassMethodMimeEvent() = default;
protected:
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const override;
private:
AZStd::string m_className;
AZStd::string m_methodName;
};
class ClassMethodEventPaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
private:
static const QString& GetDefaultIcon();
public:
AZ_CLASS_ALLOCATOR(ClassMethodEventPaletteTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(ClassMethodEventPaletteTreeItem, "{96F93970-F38A-4F08-8DC5-D52FCCE34E25}", GraphCanvas::DraggableNodePaletteTreeItem);
ClassMethodEventPaletteTreeItem(AZStd::string_view className, AZStd::string_view methodName);
~ClassMethodEventPaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
AZStd::string GetClassMethodName() const;
AZStd::string GetMethodName() const;
private:
QString m_className;
QString m_methodName;
};
// </ClassMethod>
// <CustomNode>
class CreateCustomNodeMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateCustomNodeMimeEvent, "{7130C89B-2F2D-493F-AA5C-8B72968D4200}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateCustomNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateCustomNodeMimeEvent() = default;
CreateCustomNodeMimeEvent(const AZ::Uuid& typeId);
CreateCustomNodeMimeEvent(const AZ::Uuid& typeId, const AZStd::string& styleOverride, const AZStd::string& titlePalette);
~CreateCustomNodeMimeEvent() = default;
protected:
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const override;
private:
AZ::Uuid m_typeId;
AZStd::string m_styleOverride;
AZStd::string m_titlePalette;
};
class CustomNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(CustomNodePaletteTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(CustomNodePaletteTreeItem, "{50E75C4D-F59C-4AF6-A6A3-5BAD557E335C}", GraphCanvas::DraggableNodePaletteTreeItem);
CustomNodePaletteTreeItem(const AZ::Uuid& typeId, AZStd::string_view nodeName);
~CustomNodePaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
AZ::Uuid GetTypeId() const;
private:
AZ::Uuid m_typeId;
};
// </CustomNode>
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,199 @@
/*
* 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 <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeCategorizer.h>
#include <ScriptEvents/ScriptEventsAsset.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModelBus.h>
#include <ScriptCanvas/Asset/Functions/ScriptCanvasFunctionAsset.h>
#include <ScriptCanvas/Core/Core.h>
namespace ScriptCanvasEditor
{
// Move these down into GraphCanvas for more general re-use
struct NodePaletteModelInformation
{
AZ_RTTI(NodeModelInformation, "{CC031806-7610-4C29-909D-9527F265E014}");
AZ_CLASS_ALLOCATOR(NodePaletteModelInformation, AZ::SystemAllocator, 0);
virtual ~NodePaletteModelInformation() = default;
void PopulateTreeItem(GraphCanvas::NodePaletteTreeItem& treeItem) const;
ScriptCanvas::NodeTypeIdentifier m_nodeIdentifier;
AZStd::string m_displayName;
AZStd::string m_toolTip;
AZStd::string m_categoryPath;
AZStd::string m_styleOverride;
AZStd::string m_titlePaletteOverride;
};
struct CategoryInformation
{
AZStd::string m_styleOverride;
AZStd::string m_paletteOverride = GraphCanvas::NodePaletteTreeItem::DefaultNodeTitlePalette;
AZStd::string m_tooltip;
};
class NodePaletteModel
: public GraphCanvas::CategorizerInterface
{
public:
typedef AZStd::unordered_map< ScriptCanvas::NodeTypeIdentifier, NodePaletteModelInformation* > NodePaletteRegistry;
AZ_CLASS_ALLOCATOR(NodePaletteModel, AZ::SystemAllocator, 0);
NodePaletteModel();
~NodePaletteModel();
NodePaletteId GetNotificationId() const;
void AssignAssetModel(AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel);
void RepopulateModel();
void RegisterCustomNode(AZStd::string_view categoryPath, const AZ::Uuid& uuid, AZStd::string_view name, const AZ::SerializeContext::ClassData* classData);
void RegisterClassNode(const AZStd::string& categoryPath, const AZStd::string& methodClass, const AZStd::string& methodName, AZ::BehaviorMethod* behaviorMethod, AZ::BehaviorContext* behaviorContext);
void RegisterEBusHandlerNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const AZ::BehaviorEBusHandler::BusForwarderEvent& forwardEvent);
void RegisterEBusSenderNodeModelInformation(AZStd::string_view categoryPath, AZStd::string_view busName, AZStd::string_view eventName, const ScriptCanvas::EBusBusId& busId, const ScriptCanvas::EBusEventId& eventId, const AZ::BehaviorEBusEventSender& eventSender);
// Asset Based Registrations
AZStd::vector<ScriptCanvas::NodeTypeIdentifier> RegisterScriptEvent(ScriptEvents::ScriptEventsAsset* scriptEventAsset);
AZStd::vector<ScriptCanvas::NodeTypeIdentifier> RegisterFunctionInformation(ScriptCanvas::ScriptCanvasFunctionAsset* functionAsset);
void RegisterCategoryInformation(const AZStd::string& category, const CategoryInformation& categoryInformation);
const CategoryInformation* FindCategoryInformation(const AZStd::string& categoryStyle) const;
const CategoryInformation* FindBestCategoryInformation(AZStd::string_view categoryView) const;
const NodePaletteModelInformation* FindNodePaletteInformation(const ScriptCanvas::NodeTypeIdentifier& nodeTypeIdentifier) const;
const NodePaletteRegistry& GetNodeRegistry() const;
// GraphCanvas::CategorizerInterface
GraphCanvas::GraphCanvasTreeItem* CreateCategoryNode(AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvas::GraphCanvasTreeItem* treeItem) const override;
////
// Asset Node Support
void OnRowsInserted(const QModelIndex& parentIndex, int first, int last);
void OnRowsAboutToBeRemoved(const QModelIndex& parentIndex, int first, int last);
void TraverseTree(QModelIndex index = QModelIndex());
////
private:
AZStd::vector<ScriptCanvas::NodeTypeIdentifier> ProcessAsset(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
void RemoveAsset(const AZ::Data::AssetId& assetId);
void ClearRegistry();
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_assetModel = nullptr;
AZStd::vector< QMetaObject::Connection > m_lambdaConnections;
AZStd::unordered_map< AZStd::string, CategoryInformation > m_categoryInformation;
NodePaletteRegistry m_registeredNodes;
AZStd::unordered_multimap<AZ::Data::AssetId, ScriptCanvas::NodeTypeIdentifier> m_assetMapping;
NodePaletteId m_paletteId;
};
// Concrete Sub Classes with whatever extra data is required [ScriptCanvas Only]
struct CustomNodeModelInformation
: public NodePaletteModelInformation
{
AZ_RTTI(CustomNodeModelInformation, "{481FB8AE-8683-4E50-95C1-B4B1C1B6806C}", NodePaletteModelInformation);
AZ_CLASS_ALLOCATOR(CustomNodeModelInformation, AZ::SystemAllocator, 0);
AZ::Uuid m_typeId;
};
struct MethodNodeModelInformation
: public NodePaletteModelInformation
{
AZ_RTTI(CustomNodeModelInformation, "{9B6337F9-B8D0-4B63-9EE7-91079FE386B9}", NodePaletteModelInformation);
AZ_CLASS_ALLOCATOR(CustomNodeModelInformation, AZ::SystemAllocator, 0);
AZStd::string m_classMethod;
AZStd::string m_metehodName;
};
struct EBusHandlerNodeModelInformation
: public NodePaletteModelInformation
{
AZ_RTTI(EBusNodeModelInformation, "{D1438D14-0CE9-4202-A1C5-9F5F13DFC0C4}", NodePaletteModelInformation);
AZ_CLASS_ALLOCATOR(EBusHandlerNodeModelInformation, AZ::SystemAllocator, 0);
AZStd::string m_busName;
AZStd::string m_eventName;
ScriptCanvas::EBusBusId m_busId;
ScriptCanvas::EBusEventId m_eventId;
};
struct EBusSenderNodeModelInformation
: public NodePaletteModelInformation
{
AZ_RTTI(EBusSenderNodeModelInformation, "{EE0F0385-3596-4D4E-9DC7-BE147EBB3C15}", NodePaletteModelInformation);
AZ_CLASS_ALLOCATOR(EBusHandlerNodeModelInformation, AZ::SystemAllocator, 0);
AZStd::string m_busName;
AZStd::string m_eventName;
ScriptCanvas::EBusBusId m_busId;
ScriptCanvas::EBusEventId m_eventId;
};
struct ScriptEventHandlerNodeModelInformation
: public EBusHandlerNodeModelInformation
{
AZ_RTTI(ScriptEventHandlerNodeModelInformation, "{BCA92869-63F4-4A1F-B751-F3F28443BBFC}", EBusHandlerNodeModelInformation);
AZ_CLASS_ALLOCATOR(ScriptEventHandlerNodeModelInformation, AZ::SystemAllocator, 0);
};
struct ScriptEventSenderNodeModelInformation
: public EBusSenderNodeModelInformation
{
AZ_RTTI(ScriptEventSenderNodeModelInformation, "{99046345-080C-42A6-BE76-D09583055EED}", EBusSenderNodeModelInformation);
AZ_CLASS_ALLOCATOR(ScriptEventSenderNodeModelInformation, AZ::SystemAllocator, 0);
};
//! FunctionNodeModelInformation refers to function graph assets, not methods
struct FunctionNodeModelInformation
: public NodePaletteModelInformation
{
AZ_RTTI(FunctionNodeModelInformation, "{B84B4C2C-2F0B-4C0B-879A-956E83BD2874}", NodePaletteModelInformation);
AZ_CLASS_ALLOCATOR(FunctionNodeModelInformation, AZ::SystemAllocator, 0);
AZ::Color m_functionColor;
AZ::Data::AssetId m_functionAssetId;
};
}
@@ -0,0 +1,38 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzCore/Component/EntityId.h>
#include <ScriptCanvas/Core/Core.h>
namespace ScriptCanvasEditor
{
struct NodePaletteModelInformation;
using NodePaletteId = AZ::EntityId;
class NodePaletteModelNotifications
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = AZ::EntityId;
virtual void OnAssetModelRepopulated() = 0;
virtual void OnAssetNodeAdded(NodePaletteModelInformation* nodeIdentifier) = 0;
virtual void OnAssetNodeRemoved(NodePaletteModelInformation* nodeIdentifier) = 0;
};
using NodePaletteModelNotificationBus = AZ::EBus<NodePaletteModelNotifications>;
}
@@ -0,0 +1,606 @@
/*
* 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 <qmenu.h>
#include <QPixmap>
#include <AzToolsFramework/AssetEditor/AssetEditorUtils.h>
#include <Editor/Nodes/NodeUtils.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
#include <ScriptCanvas/Libraries/Core/SendScriptEvent.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/Components/Nodes/Wrapper/WrapperNodeBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.h>
namespace ScriptCanvasEditor
{
//////////////////////////////////////
// CreateScriptEventsHandlerMimeEvent
//////////////////////////////////////
void CreateScriptEventsHandlerMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
{
serializeContext->Class<CreateScriptEventsHandlerMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(1)
->Field("m_assetId", &CreateScriptEventsHandlerMimeEvent::m_assetId)
;
}
}
CreateScriptEventsHandlerMimeEvent::CreateScriptEventsHandlerMimeEvent(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition)
: m_assetId(assetId)
, m_methodDefinition(methodDefinition)
{
}
ScriptCanvasEditor::NodeIdPair CreateScriptEventsHandlerMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
return Nodes::CreateScriptEventReceiverNode(scriptCanvasId, m_assetId);
}
bool CreateScriptEventsHandlerMimeEvent::ExecuteEvent([[maybe_unused]] const AZ::Vector2& mouseDropPosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
if (!scriptCanvasId.IsValid() || !graphCanvasGraphId.IsValid())
{
return false;
}
m_nodeIdPair = CreateNode(scriptCanvasId);
if (m_nodeIdPair.m_graphCanvasId.IsValid() && m_nodeIdPair.m_scriptCanvasId.IsValid())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, m_nodeIdPair.m_graphCanvasId, sceneDropPosition);
GraphCanvas::SceneMemberUIRequestBus::Event(m_nodeIdPair.m_graphCanvasId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
ScriptCanvasEditor::NodeCreationNotificationBus::Event(scriptCanvasId, &ScriptCanvasEditor::NodeCreationNotifications::OnGraphCanvasNodeCreated, m_nodeIdPair.m_graphCanvasId);
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
sceneDropPosition += offset;
return true;
}
else
{
if (m_nodeIdPair.m_graphCanvasId.IsValid())
{
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::DeleteEntity, m_nodeIdPair.m_graphCanvasId);
}
if (m_nodeIdPair.m_scriptCanvasId.IsValid())
{
AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::DeleteEntity, m_nodeIdPair.m_scriptCanvasId);
}
}
return false;
}
///////////////////////////////
// ScriptEventsPaletteTreeItem
///////////////////////////////
ScriptEventsPaletteTreeItem::ScriptEventsPaletteTreeItem(const AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> asset)
: GraphCanvas::NodePaletteTreeItem(asset.GetAs<ScriptEvents::ScriptEventsAsset>()->m_definition.GetName().c_str(), ScriptCanvasEditor::AssetEditorId)
, m_asset(asset)
, m_editIcon(":/ScriptCanvasEditorResources/Resources/edit_icon.png")
{
if (GetName().isEmpty())
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, m_asset.GetId());
if (assetInfo.m_relativePath.empty())
{
SetName("<Unknown Script Event>");
}
else
{
SetName(assetInfo.m_relativePath.c_str());
}
}
PopulateEvents(m_asset);
AZ::Data::AssetBus::Handler::BusConnect(m_asset.GetId());
}
ScriptEventsPaletteTreeItem::~ScriptEventsPaletteTreeItem()
{
AZ::Data::AssetBus::Handler::BusDisconnect();
}
const ScriptEvents::ScriptEvent& ScriptEventsPaletteTreeItem::GetBusDefinition() const
{
ScriptEvents::ScriptEventsAsset* ScriptEventsAsset = m_asset.GetAs<ScriptEvents::ScriptEventsAsset>();
return ScriptEventsAsset->m_definition;
}
void ScriptEventsPaletteTreeItem::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
ScriptEvents::ScriptEventsAsset* data = asset.GetAs<ScriptEvents::ScriptEventsAsset>();
if (data)
{
const ScriptEvents::ScriptEvent* previousDefinition = nullptr;
ScriptEvents::ScriptEventsAsset* previousData = m_asset.GetAs<ScriptEvents::ScriptEventsAsset>();
if (previousData)
{
previousDefinition = &data->m_definition;
}
const ScriptEvents::ScriptEvent& definition = data->m_definition;
bool recategorize = previousDefinition ? definition.GetCategory().compare(previousDefinition->GetCategory()) != 0 : false;
AZ_Warning("ScriptCanvas", !recategorize, "Unable to recategorize ScriptEvents events while open. Please close and re-open the Script Canvas Editor to see the new categorization");
if (definition.GetName().empty())
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, m_asset.GetId());
if (assetInfo.m_relativePath.empty())
{
SetName("<Unknown Script Event>");
}
else
{
SetName(assetInfo.m_relativePath.c_str());
}
}
else
{
SetName(definition.GetName().c_str());
SetToolTip(definition.GetTooltip().c_str());
}
PopulateEvents(asset);
m_asset = asset;
}
}
QVariant ScriptEventsPaletteTreeItem::OnData(const QModelIndex& index, int role) const
{
if (index.column() == NodePaletteTreeItem::Column::Customization)
{
if (IsHovered())
{
if (role == Qt::DecorationRole)
{
return m_editIcon;
}
else if (role == Qt::ToolTipRole)
{
ScriptEvents::ScriptEventsAsset* data = m_asset.GetAs<ScriptEvents::ScriptEventsAsset>();
if (data)
{
const ScriptEvents::ScriptEvent& definition = data->m_definition;
return QString("Opens the Script Event Editor to edit the Script Event - %1.").arg(definition.GetName().c_str());
}
}
}
}
return GraphCanvas::NodePaletteTreeItem::OnData(index, role);
}
void ScriptEventsPaletteTreeItem::OnHoverStateChanged()
{
SignalDataChanged();
}
void ScriptEventsPaletteTreeItem::OnClicked(int row)
{
if (row == NodePaletteTreeItem::Column::Customization)
{
AzToolsFramework::OpenGenericAssetEditor(azrtti_typeid<ScriptEvents::ScriptEventsAsset>(), m_asset.GetId());
}
}
bool ScriptEventsPaletteTreeItem::OnDoubleClicked(int row)
{
if (row != NodePaletteTreeItem::Column::Customization)
{
AzToolsFramework::OpenGenericAssetEditor(azrtti_typeid<ScriptEvents::ScriptEventsAsset>(), m_asset.GetId());
return true;
}
return false;
}
void ScriptEventsPaletteTreeItem::PopulateEvents(AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> asset)
{
ClearChildren();
ScriptEvents::ScriptEventsAsset* data = asset.GetAs<ScriptEvents::ScriptEventsAsset>();
if (data)
{
const ScriptEvents::ScriptEvent& definition = data->m_definition;
for (const ScriptEvents::Method& methodDefinition : definition.GetMethods())
{
ScriptCanvas::EBusEventId eventId = ScriptCanvas::EBusEventId(methodDefinition.GetNameProperty().GetId().ToString<AZStd::string>().c_str());
CreateChildNode<ScriptEventsEventNodePaletteTreeItem>(asset.GetId(), methodDefinition, eventId);
}
}
}
/////////////////////////////////////////////
// CreateScriptEventsReceiverEventMimeEvent
/////////////////////////////////////////////
void CreateScriptEventsReceiverMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
{
serializeContext->Class<CreateScriptEventsReceiverMimeEvent, SpecializedCreateNodeMimeEvent>()
->Version(2)
->Field("AssetId", &CreateScriptEventsReceiverMimeEvent::m_assetId)
->Field("MethodDefinition", &CreateScriptEventsReceiverMimeEvent::m_methodDefinition)
;
}
}
CreateScriptEventsReceiverMimeEvent::CreateScriptEventsReceiverMimeEvent(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition)
: m_assetId(assetId)
, m_methodDefinition(methodDefinition)
{
m_asset = AZ::Data::AssetManager::Instance().GetAsset<ScriptEvents::ScriptEventsAsset>(m_assetId, m_asset.GetAutoLoadBehavior());
}
NodeIdPair CreateScriptEventsReceiverMimeEvent::ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition)
{
NodeIdPair eventNode = CreateEventNode(graphCanvasGraphId, scenePosition);
CreateScriptEventsHandlerMimeEvent ebusMimeEvent(m_asset.GetId(), m_methodDefinition);
AZ::Vector2 position = scenePosition;
if (ebusMimeEvent.ExecuteEvent(position, position, graphCanvasGraphId))
{
NodeIdPair handlerNode = ebusMimeEvent.GetCreatedPair();
GraphCanvas::WrappedNodeConfiguration configuration;
EBusHandlerNodeDescriptorRequestBus::EventResult(configuration, handlerNode.m_graphCanvasId, &EBusHandlerNodeDescriptorRequests::GetEventConfiguration, m_methodDefinition.GetEventId());
GraphCanvas::WrapperNodeRequestBus::Event(handlerNode.m_graphCanvasId, &GraphCanvas::WrapperNodeRequests::WrapNode, eventNode.m_graphCanvasId, configuration);
}
return eventNode;
}
bool CreateScriptEventsReceiverMimeEvent::ExecuteEvent([[maybe_unused]] const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
NodeIdPair eventNode = CreateEventNode(graphCanvasGraphId, sceneDropPosition);
if (eventNode.m_graphCanvasId.IsValid())
{
GraphCanvas::SceneMemberUIRequestBus::Event(eventNode.m_graphCanvasId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
ScriptCanvasEditor::NodeCreationNotificationBus::Event(scriptCanvasId, &ScriptCanvasEditor::NodeCreationNotifications::OnGraphCanvasNodeCreated, eventNode.m_graphCanvasId);
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
sceneDropPosition += offset;
}
return eventNode.m_graphCanvasId.IsValid();
}
NodeIdPair CreateScriptEventsReceiverMimeEvent::CreateEventNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) const
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
NodeIdPair nodeIdPair;
nodeIdPair.m_graphCanvasId = Nodes::DisplayScriptEventNode(graphCanvasGraphId, m_asset.GetId(), m_methodDefinition);
if (nodeIdPair.m_graphCanvasId.IsValid())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, nodeIdPair.m_graphCanvasId, scenePosition);
}
return nodeIdPair;
}
///////////////////////////////////////////
// ScriptEventsHandlerEventPaletteTreeItem
///////////////////////////////////////////
ScriptEventsHandlerEventPaletteTreeItem::ScriptEventsHandlerEventPaletteTreeItem(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition)
: GraphCanvas::DraggableNodePaletteTreeItem(methodDefinition.GetName(), ScriptCanvasEditor::AssetEditorId)
, m_assetId(assetId)
, m_methodDefinition(methodDefinition)
{
SetToolTip(m_definition.GetTooltip().c_str());
SetTitlePalette("HandlerNodeTitlePalette");
}
GraphCanvas::GraphCanvasMimeEvent* ScriptEventsHandlerEventPaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateScriptEventsReceiverMimeEvent(m_assetId, m_methodDefinition);
}
//////////////////////////////////////////
// CreateScriptEventsSenderMimeEvent
//////////////////////////////////////////
void CreateScriptEventsSenderMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
{
serializeContext->Class<CreateScriptEventsSenderMimeEvent, CreateNodeMimeEvent>()
->Version(2)
->Field("AssetId", &CreateScriptEventsSenderMimeEvent::m_assetId)
->Field("EventDefinition", &CreateScriptEventsSenderMimeEvent::m_methodDefinition)
;
}
}
CreateScriptEventsSenderMimeEvent::CreateScriptEventsSenderMimeEvent(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition)
: m_assetId(assetId)
, m_methodDefinition(methodDefinition)
{
}
const AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> CreateScriptEventsSenderMimeEvent::GetAsset(AZ::Data::AssetLoadBehavior loadBehavior)
{
return AZ::Data::AssetManager::Instance().GetAsset<ScriptEvents::ScriptEventsAsset>(m_assetId, loadBehavior);
}
ScriptCanvasEditor::NodeIdPair CreateScriptEventsSenderMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
return Nodes::CreateScriptEventSenderNode(scriptCanvasId, m_assetId, m_methodDefinition.GetEventId());
}
/////////////////////////////////////
// ScriptEventsSenderPaletteTreeItem
/////////////////////////////////////
ScriptEventsSenderPaletteTreeItem::ScriptEventsSenderPaletteTreeItem(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition)
: GraphCanvas::DraggableNodePaletteTreeItem(methodDefinition.GetName(), ScriptCanvasEditor::AssetEditorId)
, m_assetId(assetId)
, m_methodDefinition(methodDefinition)
{
SetToolTip(m_methodDefinition.GetTooltip().c_str());
SetTitlePalette("MethodNodeTitlePalette");
}
GraphCanvas::GraphCanvasMimeEvent* ScriptEventsSenderPaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateScriptEventsSenderMimeEvent(m_assetId, m_methodDefinition);
}
/////////////////////////////////////////////////
// CreateSendOrReceiveScriptEventsMimeEvent
/////////////////////////////////////////////////
void CreateSendOrReceiveScriptEventsMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext))
{
serializeContext->Class<CreateSendOrReceiveScriptEventsMimeEvent, SpecializedCreateNodeMimeEvent>()
->Version(1)
->Field("AssetId", &CreateSendOrReceiveScriptEventsMimeEvent::m_assetId)
->Field("MethodDefinition", &CreateSendOrReceiveScriptEventsMimeEvent::m_methodDefinition)
->Field("EventId", &CreateSendOrReceiveScriptEventsMimeEvent::m_eventId)
;
}
}
CreateSendOrReceiveScriptEventsMimeEvent::CreateSendOrReceiveScriptEventsMimeEvent(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition, const ScriptCanvas::EBusEventId& eventId)
: m_assetId(assetId)
, m_methodDefinition(methodDefinition)
, m_eventId(eventId)
{
m_asset = AZ::Data::AssetManager::Instance().GetAsset<ScriptEvents::ScriptEventsAsset>(m_assetId, m_asset.GetAutoLoadBehavior());
}
bool CreateSendOrReceiveScriptEventsMimeEvent::ExecuteEvent([[maybe_unused]] const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
NodeIdPair nodeId = ConstructNode(graphCanvasGraphId, sceneDropPosition);
if (nodeId.m_graphCanvasId.IsValid())
{
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
sceneDropPosition += offset;
}
return nodeId.m_graphCanvasId.IsValid();
}
ScriptCanvasEditor::NodeIdPair CreateSendOrReceiveScriptEventsMimeEvent::ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
ScriptCanvasEditor::NodeIdPair nodeIdPair;
AZ::EntityId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::GraphCanvasGraphicsView* graphicsView = nullptr;
GraphCanvas::ViewRequestBus::EventResult(graphicsView, viewId, &GraphCanvas::ViewRequests::AsGraphicsView);
if (graphicsView)
{
QMenu menu(graphicsView);
QAction* createSender = new QAction(QString("Send %1").arg(m_methodDefinition.GetName().c_str()), &menu);
const QPixmap* senderIconPixmap;
GraphCanvas::StyleManagerRequestBus::EventResult(senderIconPixmap, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetPaletteIcon, "NodePaletteTypeIcon", "MethodNodeTitlePalette");
QIcon senderIcon((*senderIconPixmap));
createSender->setIcon(senderIcon);
menu.addAction(createSender);
QAction* createReceiver = new QAction(QString("Receive %1").arg(m_methodDefinition.GetName().c_str()), &menu);
const QPixmap* receiverIconPixmap;
GraphCanvas::StyleManagerRequestBus::EventResult(receiverIconPixmap, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetPaletteIcon, "NodePaletteTypeIcon", "HandlerNodeTitlePalette");
QIcon receiverIcon((*receiverIconPixmap));
createReceiver->setIcon(receiverIcon);
menu.addAction(createReceiver);
QAction* result = menu.exec(QCursor::pos());
if (result == createSender)
{
CreateScriptEventsSenderMimeEvent createEBusSenderNode(m_assetId, m_methodDefinition);
nodeIdPair = createEBusSenderNode.CreateNode(scriptCanvasId);
if (nodeIdPair.m_graphCanvasId.IsValid())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, nodeIdPair.m_graphCanvasId, scenePosition);
}
}
else if (result == createReceiver)
{
CreateScriptEventsReceiverMimeEvent createEBusHandlerNode(m_assetId, m_methodDefinition);
nodeIdPair = createEBusHandlerNode.ConstructNode(graphCanvasGraphId, scenePosition);
}
if (nodeIdPair.m_graphCanvasId.IsValid())
{
GraphCanvas::SceneMemberUIRequestBus::Event(nodeIdPair.m_graphCanvasId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
}
}
return nodeIdPair;
}
AZStd::vector< GraphCanvas::GraphCanvasMimeEvent* > CreateSendOrReceiveScriptEventsMimeEvent::CreateMimeEvents() const
{
AZStd::vector< GraphCanvas::GraphCanvasMimeEvent* > mimeEvents;
mimeEvents.push_back(aznew CreateScriptEventsSenderMimeEvent(m_assetId, m_methodDefinition));
mimeEvents.push_back(aznew CreateScriptEventsReceiverMimeEvent(m_assetId, m_methodDefinition));
return mimeEvents;
}
////////////////////////////////////////
// ScriptEventsEventNodePaletteTreeItem
////////////////////////////////////////
ScriptEventsEventNodePaletteTreeItem::ScriptEventsEventNodePaletteTreeItem(const AZ::Data::AssetId& assetId, const ScriptEvents::Method& methodDefinition, const ScriptCanvas::EBusEventId& eventId)
: GraphCanvas::DraggableNodePaletteTreeItem(methodDefinition.GetName().c_str(), ScriptCanvasEditor::AssetEditorId)
, m_editIcon(":/ScriptCanvasEditorResources/Resources/edit_icon.png")
, m_assetId(assetId)
, m_methodDefinition(methodDefinition)
, m_eventId(eventId)
{
m_asset = AZ::Data::AssetManager::Instance().GetAsset<ScriptEvents::ScriptEventsAsset>(assetId, m_asset.GetAutoLoadBehavior());
SetToolTip(m_methodDefinition.GetTooltip().c_str());
SetTitlePalette("MethodNodeTitlePalette");
AddIconColorPalette("HandlerNodeTitlePalette");
}
GraphCanvas::GraphCanvasMimeEvent* ScriptEventsEventNodePaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateSendOrReceiveScriptEventsMimeEvent(m_assetId, m_methodDefinition, m_eventId);
}
QVariant ScriptEventsEventNodePaletteTreeItem::OnData(const QModelIndex& index, int role) const
{
if (index.column() == NodePaletteTreeItem::Column::Customization)
{
if (IsHovered())
{
if (role == Qt::DecorationRole)
{
return m_editIcon;
}
else if (role == Qt::ToolTipRole)
{
const ScriptEvents::ScriptEvent& definition = m_asset.Get()->m_definition;
return QString("Opens the Script Events Editor to edit the Script Event - %1::%2.").arg(definition.GetName().c_str()).arg(m_methodDefinition.GetName().c_str());
}
}
}
return GraphCanvas::DraggableNodePaletteTreeItem::OnData(index, role);
}
ScriptCanvas::EBusBusId ScriptEventsEventNodePaletteTreeItem::GetBusIdentifier() const
{
return ScriptCanvas::EBusBusId(m_assetId.ToString<AZStd::string>().c_str());
}
ScriptCanvas::EBusEventId ScriptEventsEventNodePaletteTreeItem::GetEventIdentifier() const
{
return m_eventId;
}
void ScriptEventsEventNodePaletteTreeItem::OnHoverStateChanged()
{
SignalDataChanged();
}
void ScriptEventsEventNodePaletteTreeItem::OnClicked(int row)
{
if (row == NodePaletteTreeItem::Column::Customization)
{
AzToolsFramework::OpenGenericAssetEditor(azrtti_typeid<ScriptEvents::ScriptEventsAsset>(), m_assetId);
}
}
bool ScriptEventsEventNodePaletteTreeItem::OnDoubleClicked(int row)
{
if (row != NodePaletteTreeItem::Column::Customization)
{
AzToolsFramework::OpenGenericAssetEditor(azrtti_typeid<ScriptEvents::ScriptEventsAsset>(), m_asset.GetId());
return true;
}
return false;
}
}
@@ -0,0 +1,264 @@
/*
* 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 <QIcon>
#include <AzCore/Asset/AssetCommon.h>
#include <GraphCanvas/Widgets/GraphCanvasMimeEvent.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include <ScriptCanvas/Bus/NodeIdPair.h>
#include <ScriptEvents/ScriptEventsAsset.h>
#include <Editor/View/Widgets/NodePalette/CreateNodeMimeEvent.h>
#include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
namespace AZ
{
class ReflectContext;
}
namespace ScriptCanvasEditor
{
// <ScriptEventsHandlerMimeEvent>
// Handles the EBus wrapper
class CreateScriptEventsHandlerMimeEvent
: public GraphCanvas::GraphCanvasMimeEvent
{
public:
AZ_RTTI(CreateScriptEventsHandlerMimeEvent, "{4734F4B6-5915-4AEF-92A3-25FE3DBB6700}", GraphCanvas::GraphCanvasMimeEvent);
AZ_CLASS_ALLOCATOR(CreateScriptEventsHandlerMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateScriptEventsHandlerMimeEvent() = default;
CreateScriptEventsHandlerMimeEvent(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition);
~CreateScriptEventsHandlerMimeEvent() = default;
bool ExecuteEvent(const AZ::Vector2& mouseDropPosition, AZ::Vector2& dropPosition, const AZ::EntityId& graphCanvasGraphId) override final;
const ScriptCanvasEditor::NodeIdPair& GetCreatedPair() const { return m_nodeIdPair; }
protected:
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const;
private:
AZ::Data::AssetId m_assetId;
ScriptEvents::Method m_methodDefinition;
NodeIdPair m_nodeIdPair;
};
class ScriptEventsPaletteTreeItem
: public GraphCanvas::NodePaletteTreeItem
, public AZ::Data::AssetBus::Handler
{
public:
AZ_RTTI(ScriptEventsPaletteTreeItem, "{50839A0D-5FD4-4964-BEA2-CB9A74A50477}", GraphCanvas::NodePaletteTreeItem);
AZ_CLASS_ALLOCATOR(ScriptEventsPaletteTreeItem, AZ::SystemAllocator, 0);
ScriptEventsPaletteTreeItem(const AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> asset);
~ScriptEventsPaletteTreeItem() override;
const ScriptEvents::ScriptEvent& GetBusDefinition() const;
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> GetAsset() const { return m_asset; }
// AZ::Data::AssetBus::Handler
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
////
QVariant OnData(const QModelIndex& index, int role) const override;
protected:
void OnHoverStateChanged() override;
void OnClicked(int row) override;
bool OnDoubleClicked(int row) override;
private:
void PopulateEvents(AZ::Data::Asset<ScriptEvents::ScriptEventsAsset>);
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> m_asset;
QIcon m_editIcon;
};
// </ScriptEventsHandlerMimeEvent>
//<CreateScriptEventsReceiverEventMimeEvent>
// This one is for handling the events
class CreateScriptEventsReceiverMimeEvent
: public SpecializedCreateNodeMimeEvent
{
public:
AZ_RTTI(CreateScriptEventsReceiverMimeEvent, "{F957AF1F-55D9-4D85-AC92-EBFABCDF9D96}", SpecializedCreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateScriptEventsReceiverMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateScriptEventsReceiverMimeEvent() = default;
CreateScriptEventsReceiverMimeEvent(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition);
~CreateScriptEventsReceiverMimeEvent() = default;
NodeIdPair ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) override;
bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId) override;
NodeIdPair CreateEventNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) const;
private:
AZ::Data::AssetId m_assetId;
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> m_asset;
ScriptEvents::Method m_methodDefinition;
};
class ScriptEventsHandlerEventPaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_RTTI(ScriptEventsHandlerEventPaletteTreeItem, "{0E96CD24-C5DB-491C-9A3E-6EE82F73ADBA}", GraphCanvas::DraggableNodePaletteTreeItem);
AZ_CLASS_ALLOCATOR(ScriptEventsHandlerEventPaletteTreeItem, AZ::SystemAllocator, 0);
ScriptEventsHandlerEventPaletteTreeItem(const AZ::Data::AssetId assetId, const ScriptEvents::Method& m_methodDefinition);
~ScriptEventsHandlerEventPaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const;
private:
AZ::Data::AssetId m_assetId;
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> m_asset;
ScriptEvents::ScriptEvent m_definition;
ScriptEvents::Method m_methodDefinition;
};
//</CreateScriptEventsReceiverEventMimeEvent>
//<CreateScriptEventsSenderEventMimeEvent>
// This one is for sending the events
class CreateScriptEventsSenderMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateScriptEventsSenderMimeEvent, "{9D9146EB-5FA9-4C07-BFC7-399F4F3964E4}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateScriptEventsSenderMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateScriptEventsSenderMimeEvent() = default;
CreateScriptEventsSenderMimeEvent(const AZ::Data::AssetId assetId, const ScriptEvents::Method& methodDefinition);
~CreateScriptEventsSenderMimeEvent() = default;
const AZStd::string_view GetEventName() { return m_methodDefinition.GetName().c_str(); }
const AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> GetAsset(AZ::Data::AssetLoadBehavior loadBehavior);
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const override;
private:
AZ::Data::AssetId m_assetId;
ScriptEvents::Method m_methodDefinition;
};
class ScriptEventsSenderPaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_RTTI(ScriptEventsSenderPaletteTreeItem, "{0E27EB7A-9F52-4A4E-9D63-28FFAD82310B}", GraphCanvas::DraggableNodePaletteTreeItem);
AZ_CLASS_ALLOCATOR(ScriptEventsSenderPaletteTreeItem, AZ::SystemAllocator, 0);
ScriptEventsSenderPaletteTreeItem(const AZ::Data::AssetId assetId, const ScriptEvents::Method& eventDefinition);
~ScriptEventsSenderPaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const;
private:
AZ::Data::AssetId m_assetId;
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> m_asset;
ScriptEvents::Method m_methodDefinition;
};
//</CreateScriptEventsSenderEventMimeEvent>
// <CreateSendOrReceiveScriptEventsEventMimeEvent>
class CreateSendOrReceiveScriptEventsMimeEvent
: public MultiCreateNodeMimeEvent
{
public:
AZ_RTTI(CreateSendOrReceiveScriptEventsMimeEvent, "{355FC877-358E-41AF-A78C-16A7DCE0550D}", MultiCreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateSendOrReceiveScriptEventsMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateSendOrReceiveScriptEventsMimeEvent() = default;
CreateSendOrReceiveScriptEventsMimeEvent(const AZ::Data::AssetId asset, const ScriptEvents::Method& methodDefinition, const ScriptCanvas::EBusEventId& eventId);
~CreateSendOrReceiveScriptEventsMimeEvent() = default;
bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId) override;
ScriptCanvasEditor::NodeIdPair ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) override;
AZStd::vector< GraphCanvas::GraphCanvasMimeEvent* > CreateMimeEvents() const override;
private:
AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> m_asset;
const AZ::Data::AssetId m_assetId;
ScriptEvents::Method m_methodDefinition;
ScriptCanvas::EBusEventId m_eventId;
};
class ScriptEventsEventNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_RTTI(ScriptEventsEventNodePaletteTreeItem, "{C6528466-C1FF-43BE-B292-21D8F8AA7C24}", GraphCanvas::DraggableNodePaletteTreeItem);
AZ_CLASS_ALLOCATOR(ScriptEventsEventNodePaletteTreeItem, AZ::SystemAllocator, 0);
ScriptEventsEventNodePaletteTreeItem(const AZ::Data::AssetId& m_assetId, const ScriptEvents::Method& methodDefinition, const ScriptCanvas::EBusEventId& eventId);
~ScriptEventsEventNodePaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const;
QVariant OnData(const QModelIndex& index, int role) const;
ScriptCanvas::EBusBusId GetBusIdentifier() const;
ScriptCanvas::EBusEventId GetEventIdentifier() const;
protected:
void OnHoverStateChanged() override;
void OnClicked(int row) override;
bool OnDoubleClicked(int row) override;
private:
QIcon m_editIcon;
AZ::Data::AssetId m_assetId;
ScriptCanvas::EBusEventId m_eventId;
mutable AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> m_asset;
ScriptEvents::Method m_methodDefinition;
};
//
}
@@ -0,0 +1,215 @@
/*
* 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 <AzCore/Serialization/SerializeContext.h>
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/GraphCanvasBus.h>
#include "SpecializedNodePaletteTreeItemTypes.h"
#include "Editor/Components/IconComponent.h"
#include "Editor/Nodes/NodeUtils.h"
#include "ScriptCanvas/Bus/RequestBus.h"
#include "Editor/Include/ScriptCanvas/GraphCanvas/NodeDescriptorBus.h"
#include "Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h"
#include <Core/Attributes.h>
#include <Libraries/Entity/EntityRef.h>
namespace ScriptCanvasEditor
{
/////////////////////////////////
// CreateEntityRefNodeMimeEvent
/////////////////////////////////
void CreateEntityRefNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateEntityRefNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
->Field("EntityId", &CreateEntityRefNodeMimeEvent::m_entityId)
;
}
}
CreateEntityRefNodeMimeEvent::CreateEntityRefNodeMimeEvent(const AZ::EntityId& entityId)
: m_entityId(entityId)
{
}
ScriptCanvasEditor::NodeIdPair CreateEntityRefNodeMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
return Nodes::CreateEntityNode(m_entityId, scriptCanvasId);
}
/////////////////////////////////
// EntityRefNodePaletteTreeItem
/////////////////////////////////
EntityRefNodePaletteTreeItem::EntityRefNodePaletteTreeItem(AZStd::string_view nodeName, [[maybe_unused]] const QString& iconPath)
: DraggableNodePaletteTreeItem(nodeName, ScriptCanvasEditor::AssetEditorId)
{
}
GraphCanvas::GraphCanvasMimeEvent* EntityRefNodePaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateEntityRefNodeMimeEvent();
}
///////////////////////////////
// CreateCommentNodeMimeEvent
///////////////////////////////
void CreateCommentNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateCommentNodeMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
;
}
}
NodeIdPair CreateCommentNodeMimeEvent::ConstructNode(const GraphCanvas::GraphId& sceneId, const AZ::Vector2& scenePosition)
{
NodeIdPair retVal;
AZ::Entity* graphCanvasEntity = nullptr;
GraphCanvas::GraphCanvasRequestBus::BroadcastResult(graphCanvasEntity, &GraphCanvas::GraphCanvasRequests::CreateCommentNodeAndActivate);
if (graphCanvasEntity)
{
retVal.m_graphCanvasId = graphCanvasEntity->GetId();
GraphCanvas::SceneRequestBus::Event(sceneId, &GraphCanvas::SceneRequests::AddNode, graphCanvasEntity->GetId(), scenePosition);
GraphCanvas::SceneMemberUIRequestBus::Event(graphCanvasEntity->GetId(), &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
}
return retVal;
}
bool CreateCommentNodeMimeEvent::ExecuteEvent([[maybe_unused]] const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const GraphCanvas::GraphId& graphId)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphId);
NodeIdPair nodeId = ConstructNode(graphId, sceneDropPosition);
if (nodeId.m_graphCanvasId.IsValid())
{
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphId, &GraphCanvas::SceneRequests::GetGrid);
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
sceneDropPosition += offset;
}
return nodeId.m_graphCanvasId.IsValid();
}
///////////////////////////////
// CommentNodePaletteTreeItem
///////////////////////////////
CommentNodePaletteTreeItem::CommentNodePaletteTreeItem(AZStd::string_view nodeName, [[maybe_unused]] const QString& iconPath)
: DraggableNodePaletteTreeItem(nodeName, ScriptCanvasEditor::AssetEditorId)
{
SetToolTip("Comment box for notes. Does not affect script execution or data.");
SetTitlePalette("CommentNodeTitlePalette");
}
GraphCanvas::GraphCanvasMimeEvent* CommentNodePaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateCommentNodeMimeEvent();
}
/////////////////////////////
// CreateNodeGroupMimeEvent
/////////////////////////////
void CreateNodeGroupMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateNodeGroupMimeEvent, GraphCanvas::GraphCanvasMimeEvent>()
->Version(0)
;
}
}
NodeIdPair CreateNodeGroupMimeEvent::ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition)
{
NodeIdPair retVal;
AZ::Entity* graphCanvasEntity = nullptr;
GraphCanvas::GraphCanvasRequestBus::BroadcastResult(graphCanvasEntity, &GraphCanvas::GraphCanvasRequests::CreateNodeGroupAndActivate);
if (graphCanvasEntity)
{
retVal.m_graphCanvasId = graphCanvasEntity->GetId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, graphCanvasEntity->GetId(), scenePosition);
GraphCanvas::SceneMemberUIRequestBus::Event(graphCanvasEntity->GetId(), &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
}
return retVal;
}
bool CreateNodeGroupMimeEvent::ExecuteEvent([[maybe_unused]] const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
NodeIdPair nodeId = ConstructNode(graphCanvasGraphId, sceneDropPosition);
if (nodeId.m_graphCanvasId.IsValid())
{
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
sceneDropPosition += offset;
}
return nodeId.m_graphCanvasId.IsValid();
}
////////////////////////////////////
// NodeGroupNodePaletteTreeItem
////////////////////////////////////
NodeGroupNodePaletteTreeItem::NodeGroupNodePaletteTreeItem(AZStd::string_view nodeName, [[maybe_unused]] const QString& iconPath)
: DraggableNodePaletteTreeItem(nodeName, ScriptCanvasEditor::AssetEditorId)
{
}
GraphCanvas::GraphCanvasMimeEvent* NodeGroupNodePaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateNodeGroupMimeEvent();
}
}
@@ -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
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include "CreateNodeMimeEvent.h"
namespace ScriptCanvasEditor
{
// <EntityRefNode>
class CreateEntityRefNodeMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateEntityRefNodeMimeEvent, "{20CD5AF5-216E-4A41-9630-191C2803899B}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateEntityRefNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateEntityRefNodeMimeEvent() = default;
CreateEntityRefNodeMimeEvent(const AZ::EntityId& entityId);
~CreateEntityRefNodeMimeEvent() = default;
protected:
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const override;
private:
AZ::EntityId m_entityId;
};
class EntityRefNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(EntityRefNodePaletteTreeItem, AZ::SystemAllocator, 0);
EntityRefNodePaletteTreeItem(AZStd::string_view nodeName, const QString& iconPath);
~EntityRefNodePaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
};
// </EntityRefNode>
// <CommentNode>
class CreateCommentNodeMimeEvent
: public SpecializedCreateNodeMimeEvent
{
public:
AZ_RTTI(CreateCommentNodeMimeEvent, "{AF5BB1C0-E5CF-40B1-A037-1500C2BAC787}", SpecializedCreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateCommentNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateCommentNodeMimeEvent() = default;
~CreateCommentNodeMimeEvent() = default;
NodeIdPair ConstructNode(const AZ::EntityId& sceneId, const AZ::Vector2& scenePosition);
bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& sceneId) override;
};
class CommentNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(CommentNodePaletteTreeItem, AZ::SystemAllocator, 0);
CommentNodePaletteTreeItem(AZStd::string_view nodeName, const QString& iconPath);
~CommentNodePaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
};
// </CommentNode>
// <NodeGroup>
class CreateNodeGroupMimeEvent
: public SpecializedCreateNodeMimeEvent
{
public:
AZ_RTTI(CreateNodeGroupMimeEvent, "{FD969A58-404E-4B97-8A62-57C2B5EAC686}", SpecializedCreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateNodeGroupMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateNodeGroupMimeEvent() = default;
~CreateNodeGroupMimeEvent() = default;
NodeIdPair ConstructNode(const GraphCanvas::GraphId& sceneId, const AZ::Vector2& scenePosition);
bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const GraphCanvas::GraphId& sceneId) override;
};
class NodeGroupNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(NodeGroupNodePaletteTreeItem, AZ::SystemAllocator, 0);
NodeGroupNodePaletteTreeItem(AZStd::string_view nodeName, const QString& iconPath);
~NodeGroupNodePaletteTreeItem() = default;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
};
// </NodeGroup>
}
@@ -0,0 +1,671 @@
/*
* 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 <AzCore/Serialization/SerializeContext.h>
#include <QCoreApplication>
#include <qmenu.h>
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h>
#include "VariableNodePaletteTreeItemTypes.h"
#include "Editor/Components/IconComponent.h"
#include "Editor/Nodes/NodeUtils.h"
#include "Editor/Translation/TranslationHelper.h"
#include "ScriptCanvas/Bus/RequestBus.h"
#include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
#include "Editor/Include/ScriptCanvas/GraphCanvas/NodeDescriptorBus.h"
#include <Core/Attributes.h>
#include <Libraries/Core/Assign.h>
#include <Libraries/Core/BehaviorContextObjectNode.h>
#include <Libraries/Core/Method.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
namespace ScriptCanvasEditor
{
///////////////////////////////////
// CreateGetVariableNodeMimeEvent
///////////////////////////////////
void CreateGetVariableNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateGetVariableNodeMimeEvent, CreateNodeMimeEvent>()
->Version(0)
->Field("VariableId", &CreateGetVariableNodeMimeEvent::m_variableId)
;
}
}
CreateGetVariableNodeMimeEvent::CreateGetVariableNodeMimeEvent(const ScriptCanvas::VariableId& variableId)
: m_variableId(variableId)
{
}
ScriptCanvasEditor::NodeIdPair CreateGetVariableNodeMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
return Nodes::CreateGetVariableNode(m_variableId, scriptCanvasId);
}
///////////////////////////////////
// GetVariableNodePaletteTreeItem
///////////////////////////////////
const QString& GetVariableNodePaletteTreeItem::GetDefaultIcon()
{
static QString defaultIcon;
if (defaultIcon.isEmpty())
{
defaultIcon = IconComponent::LookupClassIcon(AZ::Uuid()).c_str();
}
return defaultIcon;
}
GetVariableNodePaletteTreeItem::GetVariableNodePaletteTreeItem()
: DraggableNodePaletteTreeItem("Get Variable", ScriptCanvasEditor::AssetEditorId)
{
SetToolTip("After specifying a variable name, this node will expose output slots that return the specified variable's values.\nVariable names must begin with # (for example, #MyVar).");
}
GetVariableNodePaletteTreeItem::GetVariableNodePaletteTreeItem(const ScriptCanvas::VariableId& variableId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
: DraggableNodePaletteTreeItem("", ScriptCanvasEditor::AssetEditorId)
, m_variableId(variableId)
{
AZStd::string_view variableName;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(variableName, scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::GetVariableName, variableId);
OnVariableRenamed(variableName);
ScriptCanvas::VariableNotificationBus::Handler::BusConnect(ScriptCanvas::GraphScopedVariableId(scriptCanvasId, m_variableId));
ScriptCanvas::Data::Type scriptCanvasType = ScriptCanvas::Data::Type::Invalid();
ScriptCanvas::VariableRequestBus::EventResult(scriptCanvasType, ScriptCanvas::GraphScopedVariableId(scriptCanvasId, m_variableId), &ScriptCanvas::VariableRequests::GetType);
if (scriptCanvasType.IsValid())
{
AZ::Uuid azType = ScriptCanvas::Data::ToAZType(scriptCanvasType);
AZStd::string colorPalette;
GraphCanvas::StyleManagerRequestBus::EventResult(colorPalette, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetDataPaletteStyle, azType);
SetTitlePalette(colorPalette);
}
}
GetVariableNodePaletteTreeItem::~GetVariableNodePaletteTreeItem()
{
ScriptCanvas::VariableNotificationBus::Handler::BusDisconnect();
}
void GetVariableNodePaletteTreeItem::OnVariableRenamed(AZStd::string_view variableName)
{
AZStd::string fullName = AZStd::string::format("Get %s", variableName.data());
SetName(fullName.c_str());
AZStd::string tooltip = AZStd::string::format("This node returns %s's values", variableName.data());
SetToolTip(tooltip.c_str());
}
const ScriptCanvas::VariableId& GetVariableNodePaletteTreeItem::GetVariableId() const
{
return m_variableId;
}
GraphCanvas::GraphCanvasMimeEvent* GetVariableNodePaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateGetVariableNodeMimeEvent(m_variableId);
}
///////////////////////////////////
// CreateSetVariableNodeMimeEvent
///////////////////////////////////
void CreateSetVariableNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateSetVariableNodeMimeEvent, CreateNodeMimeEvent>()
->Version(0)
->Field("VariableId", &CreateSetVariableNodeMimeEvent::m_variableId)
;
}
}
CreateSetVariableNodeMimeEvent::CreateSetVariableNodeMimeEvent(const ScriptCanvas::VariableId& variableId)
: m_variableId(variableId)
{
}
ScriptCanvasEditor::NodeIdPair CreateSetVariableNodeMimeEvent::CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
return Nodes::CreateSetVariableNode(m_variableId, scriptCanvasId);
}
///////////////////////////////////
// SetVariableNodePaletteTreeItem
///////////////////////////////////
const QString& SetVariableNodePaletteTreeItem::GetDefaultIcon()
{
static QString defaultIcon;
if (defaultIcon.isEmpty())
{
defaultIcon = IconComponent::LookupClassIcon(AZ::Uuid()).c_str();
}
return defaultIcon;
}
SetVariableNodePaletteTreeItem::SetVariableNodePaletteTreeItem()
: GraphCanvas::DraggableNodePaletteTreeItem("Set Variable", ScriptCanvasEditor::AssetEditorId)
{
SetToolTip("This node changes a variable's values according to the data connected to the input slots");
}
SetVariableNodePaletteTreeItem::SetVariableNodePaletteTreeItem(const ScriptCanvas::VariableId& variableId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
: GraphCanvas::DraggableNodePaletteTreeItem("", ScriptCanvasEditor::AssetEditorId)
, m_variableId(variableId)
{
AZStd::string_view variableName;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(variableName, scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::GetVariableName, variableId);
OnVariableRenamed(variableName);
ScriptCanvas::VariableNotificationBus::Handler::BusConnect(ScriptCanvas::GraphScopedVariableId(scriptCanvasId, m_variableId));
ScriptCanvas::Data::Type scriptCanvasType = ScriptCanvas::Data::Type::Invalid();
ScriptCanvas::VariableRequestBus::EventResult(scriptCanvasType, ScriptCanvas::GraphScopedVariableId(scriptCanvasId, m_variableId), &ScriptCanvas::VariableRequests::GetType);
if (scriptCanvasType.IsValid())
{
AZ::Uuid azType = ScriptCanvas::Data::ToAZType(scriptCanvasType);
AZStd::string colorPalette;
GraphCanvas::StyleManagerRequestBus::EventResult(colorPalette, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetDataPaletteStyle, azType);
SetTitlePalette(colorPalette);
}
}
SetVariableNodePaletteTreeItem::~SetVariableNodePaletteTreeItem()
{
ScriptCanvas::VariableNotificationBus::Handler::BusDisconnect();
}
void SetVariableNodePaletteTreeItem::OnVariableRenamed(AZStd::string_view variableName)
{
AZStd::string fullName = AZStd::string::format("Set %s", variableName.data());
SetName(fullName.c_str());
AZStd::string tooltip = AZStd::string::format("This node changes %s's values according to the data connected to the input slots", variableName.data());
SetToolTip(tooltip.c_str());
}
const ScriptCanvas::VariableId& SetVariableNodePaletteTreeItem::GetVariableId() const
{
return m_variableId;
}
GraphCanvas::GraphCanvasMimeEvent* SetVariableNodePaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateSetVariableNodeMimeEvent(m_variableId);
}
///////////////////////////////////////
// CreateVariableChangedNodeMimeEvent
///////////////////////////////////////
void CreateVariableChangedNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateVariableChangedNodeMimeEvent, CreateEBusHandlerEventMimeEvent>()
->Version(0)
->Field("VariableId", &CreateVariableChangedNodeMimeEvent::m_variableId)
;
}
}
CreateVariableChangedNodeMimeEvent::CreateVariableChangedNodeMimeEvent(const ScriptCanvas::VariableId& variableId)
: m_variableId(variableId)
{
}
bool CreateVariableChangedNodeMimeEvent::ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
ConfigureEBusEvent();
NodeIdPair nodeIdPair = CreateEBusHandlerEventMimeEvent::ExecuteEventImpl(mousePosition, sceneDropPosition, graphCanvasGraphId);
ScriptCanvas::GraphScopedVariableId scopedVariableId(ScriptCanvas::ScriptCanvasId(), m_variableId);
ScriptCanvas::Datum idDatum(ScriptCanvas::Data::FromAZType(azrtti_typeid<ScriptCanvas::GraphScopedVariableId>()), ScriptCanvas::Datum::eOriginality::Original);
idDatum.Set(AZStd::move(scopedVariableId));
EBusHandlerEventNodeDescriptorRequestBus::Event(nodeIdPair.m_graphCanvasId, &EBusHandlerEventNodeDescriptorRequests::SetHandlerAddress, idDatum);
return nodeIdPair.m_graphCanvasId.IsValid();
}
NodeIdPair CreateVariableChangedNodeMimeEvent::ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition)
{
ConfigureEBusEvent();
NodeIdPair nodeIdPair = CreateEBusHandlerEventMimeEvent::ConstructNode(graphCanvasGraphId, scenePosition);
ScriptCanvas::GraphScopedVariableId scopedVariableId(ScriptCanvas::ScriptCanvasId(), m_variableId);
ScriptCanvas::Datum idDatum(ScriptCanvas::Data::FromAZType(azrtti_typeid<ScriptCanvas::GraphScopedVariableId>()), ScriptCanvas::Datum::eOriginality::Original);
idDatum.Set(AZStd::move(scopedVariableId));
EBusHandlerEventNodeDescriptorRequestBus::Event(nodeIdPair.m_graphCanvasId, &EBusHandlerEventNodeDescriptorRequests::SetHandlerAddress, idDatum);
return nodeIdPair;
}
void CreateVariableChangedNodeMimeEvent::ConfigureEBusEvent()
{
if (GetBusName().empty())
{
AZ::BehaviorContext* behaviorContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(behaviorContext, &AZ::ComponentApplicationRequests::GetBehaviorContext);
if (behaviorContext)
{
auto busIter = behaviorContext->m_ebuses.find(ScriptCanvas::GraphVariable::GetVariableNotificationBusName());
if (busIter != behaviorContext->m_ebuses.end())
{
if (busIter->second->m_createHandler)
{
AZ::BehaviorEBusHandler* handler(nullptr);
if (busIter->second->m_createHandler->InvokeResult(handler) && handler)
{
const AZStd::vector<AZ::BehaviorEBusHandler::BusForwarderEvent>& events = handler->GetEvents();
for (auto forwarderEvent : events)
{
if (strcmp(forwarderEvent.m_name, "OnVariableValueChanged") == 0)
{
ConfigureEvent(busIter->second->m_name, forwarderEvent.m_name, forwarderEvent.m_eventId);
}
}
}
}
}
}
}
}
///////////////////////////////////////
// VariableChangedNodePaletteTreeItem
///////////////////////////////////////
const QString& VariableChangedNodePaletteTreeItem::GetDefaultIcon()
{
static QString defaultIcon;
if (defaultIcon.isEmpty())
{
defaultIcon = IconComponent::LookupClassIcon(AZ::Uuid()).c_str();
}
return defaultIcon;
}
VariableChangedNodePaletteTreeItem::VariableChangedNodePaletteTreeItem()
: GraphCanvas::DraggableNodePaletteTreeItem("On Variable Changed", ScriptCanvasEditor::AssetEditorId)
{
SetToolTip("This node changes a variable's values according to the data connected to the input slots");
}
VariableChangedNodePaletteTreeItem::VariableChangedNodePaletteTreeItem(const ScriptCanvas::VariableId& variableId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
: GraphCanvas::DraggableNodePaletteTreeItem("", ScriptCanvasEditor::AssetEditorId)
, m_variableId(variableId)
{
AZStd::string_view variableName;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(variableName, scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::GetVariableName, variableId);
OnVariableRenamed(variableName);
ScriptCanvas::VariableNotificationBus::Handler::BusConnect(ScriptCanvas::GraphScopedVariableId(scriptCanvasId, m_variableId));
ScriptCanvas::Data::Type scriptCanvasType = ScriptCanvas::Data::Type::Invalid();
ScriptCanvas::VariableRequestBus::EventResult(scriptCanvasType, ScriptCanvas::GraphScopedVariableId(scriptCanvasId, m_variableId), &ScriptCanvas::VariableRequests::GetType);
if (scriptCanvasType.IsValid())
{
AZ::Uuid azType = ScriptCanvas::Data::ToAZType(scriptCanvasType);
AZStd::string colorPalette;
GraphCanvas::StyleManagerRequestBus::EventResult(colorPalette, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetDataPaletteStyle, azType);
SetTitlePalette(colorPalette);
}
}
VariableChangedNodePaletteTreeItem::~VariableChangedNodePaletteTreeItem()
{
ScriptCanvas::VariableNotificationBus::Handler::BusDisconnect();
}
void VariableChangedNodePaletteTreeItem::OnVariableRenamed(AZStd::string_view variableName)
{
AZStd::string fullName = AZStd::string::format("On %s Changed", variableName.data());
SetName(fullName.c_str());
AZStd::string tooltip = AZStd::string::format("Signals when %s's values changes.", variableName.data());
SetToolTip(tooltip.c_str());
}
const ScriptCanvas::VariableId& VariableChangedNodePaletteTreeItem::GetVariableId() const
{
return m_variableId;
}
GraphCanvas::GraphCanvasMimeEvent* VariableChangedNodePaletteTreeItem::CreateMimeEvent() const
{
return aznew CreateVariableChangedNodeMimeEvent(m_variableId);
}
////////////////////////////////////////
// CreateVariableSpecificNodeMimeEvent
////////////////////////////////////////
void CreateVariableSpecificNodeMimeEvent::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<CreateVariableSpecificNodeMimeEvent, SpecializedCreateNodeMimeEvent>()
->Version(0)
->Field("VariableId", &CreateVariableSpecificNodeMimeEvent::m_variableId)
;
}
}
CreateVariableSpecificNodeMimeEvent::CreateVariableSpecificNodeMimeEvent(const ScriptCanvas::VariableId& variableId)
: m_variableId(variableId)
{
}
bool CreateVariableSpecificNodeMimeEvent::ExecuteEvent([[maybe_unused]] const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
NodeIdPair nodeId = ConstructNode(graphCanvasGraphId, sceneDropPosition);
if (nodeId.m_graphCanvasId.IsValid())
{
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
AZ::Vector2 offset;
GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
sceneDropPosition += offset;
}
return nodeId.m_graphCanvasId.IsValid();
}
ScriptCanvasEditor::NodeIdPair CreateVariableSpecificNodeMimeEvent::ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
ScriptCanvasEditor::NodeIdPair nodeIdPair;
AZ::EntityId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::GraphCanvasGraphicsView* graphicsView = nullptr;
GraphCanvas::ViewRequestBus::EventResult(graphicsView, viewId, &GraphCanvas::ViewRequests::AsGraphicsView);
if (graphicsView)
{
AZStd::string variableName;
ScriptCanvas::VariableRequestBus::EventResult(variableName, ScriptCanvas::GraphScopedVariableId(scriptCanvasId, m_variableId), &ScriptCanvas::VariableRequests::GetName);
QMenu menu(graphicsView);
QAction* createGet = new QAction(QString("Get %1").arg(variableName.c_str()), &menu);
menu.addAction(createGet);
QAction* createChanged = new QAction(QString("On %1 Changed").arg(variableName.c_str()), &menu);
menu.addAction(createChanged);
QAction* createSet = new QAction(QString("Set %1").arg(variableName.c_str()), &menu);
menu.addAction(createSet);
QAction* result = menu.exec(QCursor::pos());
if (result == createGet)
{
CreateGetVariableNodeMimeEvent createGetVariableNode(m_variableId);
nodeIdPair = createGetVariableNode.CreateNode(scriptCanvasId);
}
else if (result == createSet)
{
CreateSetVariableNodeMimeEvent createSetVariableNode(m_variableId);
nodeIdPair = createSetVariableNode.CreateNode(scriptCanvasId);
}
else if (result == createChanged)
{
CreateVariableChangedNodeMimeEvent createChangedVariableNode(m_variableId);
nodeIdPair = createChangedVariableNode.ConstructNode(graphCanvasGraphId, scenePosition);
}
if (nodeIdPair.m_graphCanvasId.IsValid() && nodeIdPair.m_scriptCanvasId.IsValid())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::AddNode, nodeIdPair.m_graphCanvasId, scenePosition);
GraphCanvas::SceneMemberUIRequestBus::Event(nodeIdPair.m_graphCanvasId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
}
}
return nodeIdPair;
}
AZStd::vector< GraphCanvas::GraphCanvasMimeEvent* > CreateVariableSpecificNodeMimeEvent::CreateMimeEvents() const
{
AZStd::vector< GraphCanvas::GraphCanvasMimeEvent* > mimeEvents;
mimeEvents.push_back(aznew CreateGetVariableNodeMimeEvent(m_variableId));
mimeEvents.push_back(aznew CreateSetVariableNodeMimeEvent(m_variableId));
mimeEvents.push_back(aznew CreateVariableChangedNodeMimeEvent(m_variableId));
return mimeEvents;
}
////////////////////////////////////////
// VariableCategoryNodePaletteTreeItem
////////////////////////////////////////
VariableCategoryNodePaletteTreeItem::VariableCategoryNodePaletteTreeItem(AZStd::string_view displayName)
: NodePaletteTreeItem(displayName, ScriptCanvasEditor::AssetEditorId)
{
}
void VariableCategoryNodePaletteTreeItem::PreOnChildAdded(GraphCanvasTreeItem* item)
{
// Force elements to display in the order they were added rather then alphabetical.
static_cast<NodePaletteTreeItem*>(item)->SetItemOrdering(GetChildCount());
}
//////////////////////////////////////////
// LocalVariablesListNodePaletteTreeItem
//////////////////////////////////////////
LocalVariablesListNodePaletteTreeItem::LocalVariablesListNodePaletteTreeItem(AZStd::string_view displayName)
: NodePaletteTreeItem(displayName, ScriptCanvasEditor::AssetEditorId)
{
GraphCanvas::AssetEditorNotificationBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
SetAllowPruneOnEmpty(false);
}
void LocalVariablesListNodePaletteTreeItem::OnActiveGraphChanged(const AZ::EntityId& graphCanvasGraphId)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
if (m_scriptCanvasId != scriptCanvasId)
{
if (m_scriptCanvasId.IsValid())
{
GraphItemCommandNotificationBus::Handler::BusDisconnect(m_scriptCanvasId);
ScriptCanvas::GraphVariableManagerNotificationBus::Handler::BusDisconnect(m_scriptCanvasId);
}
m_scriptCanvasId = scriptCanvasId;
if (m_scriptCanvasId.IsValid())
{
ScriptCanvas::GraphVariableManagerNotificationBus::Handler::BusConnect(m_scriptCanvasId);
GraphItemCommandNotificationBus::Handler::BusConnect(m_scriptCanvasId);
}
RefreshVariableList();
}
}
void LocalVariablesListNodePaletteTreeItem::PostRestore(const UndoData&)
{
RefreshVariableList();
}
void LocalVariablesListNodePaletteTreeItem::OnVariableAddedToGraph(const ScriptCanvas::VariableId& variableId, AZStd::string_view /*variableName*/)
{
QScopedValueRollback<bool> valueRollback(m_ignoreTreeSignals, true);
LocalVariableNodePaletteTreeItem* localVariableTreeItem = CreateChildNode<LocalVariableNodePaletteTreeItem>(variableId, m_scriptCanvasId);
localVariableTreeItem->PopulateChildren();
}
void LocalVariablesListNodePaletteTreeItem::OnVariableRemovedFromGraph(const ScriptCanvas::VariableId& variableId, AZStd::string_view /*variableName*/)
{
int rows = GetChildCount();
for (int i = 0; i < rows; ++i)
{
LocalVariableNodePaletteTreeItem* treeItem = static_cast<LocalVariableNodePaletteTreeItem*>(FindChildByRow(i));
if (treeItem->GetVariableId() == variableId)
{
QScopedValueRollback<bool> valueRollback(m_ignoreTreeSignals, true);
RemoveChild(treeItem);
break;
}
}
}
void LocalVariablesListNodePaletteTreeItem::OnChildAdded(GraphCanvas::GraphCanvasTreeItem* treeItem)
{
if (!m_ignoreTreeSignals)
{
m_nonVariableTreeItems.insert(treeItem);
}
}
void LocalVariablesListNodePaletteTreeItem::RefreshVariableList()
{
QScopedValueRollback<bool> valueRollback(m_ignoreTreeSignals, true);
for (GraphCanvas::GraphCanvasTreeItem* item : m_nonVariableTreeItems)
{
item->DetachItem();
}
// Need to let the child clear signal out
ClearChildren();
const ScriptCanvas::GraphVariableMapping* variableMapping = nullptr;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(variableMapping, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::GetVariables);
if (variableMapping != nullptr)
{
for (const auto& mapPair : (*variableMapping))
{
LocalVariableNodePaletteTreeItem* rootItem = this->CreateChildNode<LocalVariableNodePaletteTreeItem>(mapPair.first, m_scriptCanvasId);
rootItem->PopulateChildren();
}
}
for (GraphCanvas::GraphCanvasTreeItem* item : m_nonVariableTreeItems)
{
AddChild(item);
}
}
/////////////////////////////////////
// LocalVariableNodePaletteTreeItem
/////////////////////////////////////
LocalVariableNodePaletteTreeItem::LocalVariableNodePaletteTreeItem(ScriptCanvas::VariableId variableId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
: NodePaletteTreeItem("", ScriptCanvasEditor::AssetEditorId)
, m_scriptCanvasId(scriptCanvasId)
, m_variableId(variableId)
{
AZStd::string_view variableName;
ScriptCanvas::GraphVariableManagerRequestBus::EventResult(variableName, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::GetVariableName, variableId);
OnVariableRenamed(variableName);
ScriptCanvas::VariableNotificationBus::Handler::BusConnect(ScriptCanvas::GraphScopedVariableId(scriptCanvasId, variableId));
}
LocalVariableNodePaletteTreeItem::~LocalVariableNodePaletteTreeItem()
{
ScriptCanvas::VariableNotificationBus::Handler::BusDisconnect();
}
void LocalVariableNodePaletteTreeItem::PopulateChildren()
{
if (GetChildCount() == 0)
{
CreateChildNode<GetVariableNodePaletteTreeItem>(GetVariableId(), m_scriptCanvasId);
CreateChildNode<SetVariableNodePaletteTreeItem>(GetVariableId(), m_scriptCanvasId);
CreateChildNode<VariableChangedNodePaletteTreeItem>(GetVariableId(), m_scriptCanvasId);
}
}
const ScriptCanvas::VariableId& LocalVariableNodePaletteTreeItem::GetVariableId() const
{
return m_variableId;
}
void LocalVariableNodePaletteTreeItem::OnVariableRenamed(AZStd::string_view variableName)
{
AZStd::string localName(variableName);
SetName(localName.c_str());
}
}
@@ -0,0 +1,268 @@
/*
* 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 <GraphCanvas/Editor/AssetEditorBus.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/DraggableNodePaletteTreeItem.h>
#include "CreateNodeMimeEvent.h"
#include "Editor/Undo/ScriptCanvasGraphCommand.h"
#include <ScriptCanvas/Variable/VariableBus.h>
#include <Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.h>
namespace ScriptCanvasEditor
{
// <GetVariableNode>
class CreateGetVariableNodeMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateGetVariableNodeMimeEvent, "{A9784FF3-E749-4EB4-B5DB-DF510F7CD151}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateGetVariableNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateGetVariableNodeMimeEvent() = default;
explicit CreateGetVariableNodeMimeEvent(const ScriptCanvas::VariableId& variableId);
~CreateGetVariableNodeMimeEvent() = default;
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const override;
private:
ScriptCanvas::VariableId m_variableId;
};
class GetVariableNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
, public ScriptCanvas::VariableNotificationBus::Handler
{
public:
AZ_RTTI(GetVariableNodePaletteTreeItem, "{0589E084-2E57-4650-96BF-E42DA17D7731}", GraphCanvas::DraggableNodePaletteTreeItem)
AZ_CLASS_ALLOCATOR(GetVariableNodePaletteTreeItem, AZ::SystemAllocator, 0);
static const QString& GetDefaultIcon();
GetVariableNodePaletteTreeItem();
GetVariableNodePaletteTreeItem(const ScriptCanvas::VariableId& variableId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
~GetVariableNodePaletteTreeItem();
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
// VariableNotificationBus::Handler
void OnVariableRenamed(AZStd::string_view variableName) override;
////
const ScriptCanvas::VariableId& GetVariableId() const;
private:
ScriptCanvas::VariableId m_variableId;
};
// </GetVariableNode>
// <SetVariableNode>
class CreateSetVariableNodeMimeEvent
: public CreateNodeMimeEvent
{
public:
AZ_RTTI(CreateSetVariableNodeMimeEvent, "{D855EE9C-74E0-4760-AA0F-239ADF7507B6}", CreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateSetVariableNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateSetVariableNodeMimeEvent() = default;
explicit CreateSetVariableNodeMimeEvent(const ScriptCanvas::VariableId& variableId);
~CreateSetVariableNodeMimeEvent() = default;
ScriptCanvasEditor::NodeIdPair CreateNode(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const override;
private:
ScriptCanvas::VariableId m_variableId;
};
class SetVariableNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
, public ScriptCanvas::VariableNotificationBus::Handler
{
public:
AZ_RTTI(SetVariableNodePaletteTreeItem, "{BCFD5653-6621-4BAC-BD8E-71EC6190062F}", GraphCanvas::DraggableNodePaletteTreeItem)
AZ_CLASS_ALLOCATOR(SetVariableNodePaletteTreeItem, AZ::SystemAllocator, 0);
static const QString& GetDefaultIcon();
SetVariableNodePaletteTreeItem();
SetVariableNodePaletteTreeItem(const ScriptCanvas::VariableId& variableId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
~SetVariableNodePaletteTreeItem();
// VariableNotificationBus::Handler
void OnVariableRenamed(AZStd::string_view variableName) override;
////
const ScriptCanvas::VariableId& GetVariableId() const;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
private:
ScriptCanvas::VariableId m_variableId;
};
// </SetVariableNode>
// <VariableChanged>
class CreateVariableChangedNodeMimeEvent
: public CreateEBusHandlerEventMimeEvent
{
public:
AZ_RTTI(CreateVariableChangedNodeMimeEvent, "{C117AC91-FBB5-410D-BA7F-B4C15140EA6F}", CreateEBusHandlerEventMimeEvent);
AZ_CLASS_ALLOCATOR(CreateVariableChangedNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateVariableChangedNodeMimeEvent() = default;
explicit CreateVariableChangedNodeMimeEvent(const ScriptCanvas::VariableId& variableId);
~CreateVariableChangedNodeMimeEvent() = default;
bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId) override;
NodeIdPair ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) override;
private:
void ConfigureEBusEvent();
ScriptCanvas::VariableId m_variableId;
};
class VariableChangedNodePaletteTreeItem
: public GraphCanvas::DraggableNodePaletteTreeItem
, public ScriptCanvas::VariableNotificationBus::Handler
{
public:
AZ_RTTI(VariableChangedNodePaletteTreeItem, "{209D877C-9D15-4B4F-ADF0-2D1A127A4A0D}", GraphCanvas::DraggableNodePaletteTreeItem);
AZ_CLASS_ALLOCATOR(VariableChangedNodePaletteTreeItem, AZ::SystemAllocator, 0);
static const QString& GetDefaultIcon();
VariableChangedNodePaletteTreeItem();
VariableChangedNodePaletteTreeItem(const ScriptCanvas::VariableId& variableId, const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
~VariableChangedNodePaletteTreeItem();
// VariableNotificationBus::Handler
void OnVariableRenamed(AZStd::string_view variableName) override;
////
const ScriptCanvas::VariableId& GetVariableId() const;
GraphCanvas::GraphCanvasMimeEvent* CreateMimeEvent() const override;
private:
ScriptCanvas::VariableId m_variableId;
};
// </VariableChanged>
// <CreateVariableSpecificNodeMimeEvent>
class CreateVariableSpecificNodeMimeEvent
: public MultiCreateNodeMimeEvent
{
public:
AZ_RTTI(CreateVariableSpecificNodeMimeEvent, "{924C1192-C32A-4A35-B146-2739AB4383DB}", MultiCreateNodeMimeEvent);
AZ_CLASS_ALLOCATOR(CreateVariableSpecificNodeMimeEvent, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
CreateVariableSpecificNodeMimeEvent() = default;
explicit CreateVariableSpecificNodeMimeEvent(const ScriptCanvas::VariableId& variableId);
~CreateVariableSpecificNodeMimeEvent() = default;
bool ExecuteEvent(const AZ::Vector2& mousePosition, AZ::Vector2& sceneDropPosition, const AZ::EntityId& graphCanvasGraphId) override;
ScriptCanvasEditor::NodeIdPair ConstructNode(const AZ::EntityId& graphCanvasGraphId, const AZ::Vector2& scenePosition) override;
AZStd::vector< GraphCanvas::GraphCanvasMimeEvent* > CreateMimeEvents() const override;
private:
ScriptCanvas::VariableId m_variableId;
};
// </CreateVariableSpecificNodeMimeEvent>
// <VariableCategoryNodePaletteTreeItem>
class VariableCategoryNodePaletteTreeItem
: public GraphCanvas::NodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(VariableCategoryNodePaletteTreeItem, AZ::SystemAllocator, 0);
VariableCategoryNodePaletteTreeItem(AZStd::string_view displayName);
~VariableCategoryNodePaletteTreeItem() = default;
private:
void PreOnChildAdded(GraphCanvasTreeItem* item) override;
};
// </VariableNodePaeltteTreeItem>
// <LocalVariablesListNodePaletteTreeItem>
class LocalVariablesListNodePaletteTreeItem
: public GraphCanvas::NodePaletteTreeItem
, public GraphCanvas::AssetEditorNotificationBus::Handler
, public ScriptCanvas::GraphVariableManagerNotificationBus::Handler
, public GraphItemCommandNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(LocalVariablesListNodePaletteTreeItem, AZ::SystemAllocator, 0);
LocalVariablesListNodePaletteTreeItem(AZStd::string_view displayName);
~LocalVariablesListNodePaletteTreeItem() = default;
// GraphCanvas::AssetEditorNotificationBus
void OnActiveGraphChanged(const GraphCanvas::GraphId& graphCanvasGraphId) override;
////
// GraphItemCommandNotificationBus
void PostRestore(const UndoData& undoData) override;
////
// GraphVariableManagerNotificationBus
void OnVariableAddedToGraph(const ScriptCanvas::VariableId& variableId, AZStd::string_view variableName) override;
void OnVariableRemovedFromGraph(const ScriptCanvas::VariableId& variableId, AZStd::string_view variableName) override;
////
protected:
void OnChildAdded(GraphCanvas::GraphCanvasTreeItem* treeItem) override;
private:
void RefreshVariableList();
ScriptCanvas::ScriptCanvasId m_scriptCanvasId;
bool m_ignoreTreeSignals = false;
AZStd::unordered_set<GraphCanvas::GraphCanvasTreeItem*> m_nonVariableTreeItems;
};
// </LocalVariablesNodePaletteTreeItem>
// <LocalVariableNodePaletteTreeItem>
class LocalVariableNodePaletteTreeItem
: public GraphCanvas::NodePaletteTreeItem
, public ScriptCanvas::VariableNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(LocalVariableNodePaletteTreeItem, AZ::SystemAllocator, 0);
LocalVariableNodePaletteTreeItem(ScriptCanvas::VariableId variableTreeItem, const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
~LocalVariableNodePaletteTreeItem();
void PopulateChildren();
const ScriptCanvas::VariableId& GetVariableId() const;
// VariableNotificationBus
void OnVariableRenamed(AZStd::string_view) override;
////
private:
ScriptCanvas::ScriptCanvasId m_scriptCanvasId;
ScriptCanvas::VariableId m_variableId;
};
// </VariableNodePaletteTreeItem>
}
@@ -0,0 +1,700 @@
/*
* 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 "PropertyGrid.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QScrollArea>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
#include <AzToolsFramework/UI/PropertyEditor/EntityPropertyEditor.hxx>
#include <AzToolsFramework/UI/PropertyEditor/ComponentEditor.hxx>
#include <AzToolsFramework/UI/PropertyEditor/ComponentEditorHeader.hxx>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponentBus.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <Editor/View/Widgets/PropertyGridContextMenu.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
#include <GraphCanvas/Components/GraphCanvasPropertyBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Editor/GraphCanvasProfiler.h>
#include <ScriptCanvas/Core/Endpoint.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <ScriptCanvas/Libraries/Core/EBusEventHandler.h>
#include <ScriptCanvas/Libraries/Core/Method.h>
#include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
namespace
{
using StringToInstanceMap = AZStd::unordered_map<AZStd::string, ScriptCanvasEditor::Widget::PropertyGrid::InstancesToDisplay>;
AZStd::string GetTitle(const AZ::EntityId& entityId, AZ::Component* instance)
{
AZStd::string result;
AZStd::string title;
GraphCanvas::NodeTitleRequestBus::EventResult(title, entityId, &GraphCanvas::NodeTitleRequests::GetTitle);
AZStd::string subtitle;
GraphCanvas::NodeTitleRequestBus::EventResult(subtitle, entityId, &GraphCanvas::NodeTitleRequests::GetSubTitle);
// NOT a variable.
result = title;
if (!subtitle.empty())
{
result += (result.empty() ? "" : " - " ) + subtitle;
}
if (result.empty())
{
result = AzToolsFramework::GetFriendlyComponentName(instance).c_str();
}
return result;
}
void AddInstancesToComponentEditor(
AzToolsFramework::ComponentEditor* componentEditor,
const AZStd::list<AZ::Component*>& instanceList,
AZStd::unordered_map<AZ::TypeId, AZ::Component*>& firstOfTypeMap,
AZStd::unordered_set<AZ::EntityId>& entitySet)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
for (auto& instance : instanceList)
{
GRAPH_CANVAS_DETAILED_PROFILE_SCOPE("AddInstanceToComponentEditor::InnerLoop");
// non-first instances are aggregated under the first instance
AZ::Component* aggregateInstance = nullptr;
if (firstOfTypeMap.count(instance->RTTI_GetType()) > 0)
{
aggregateInstance = firstOfTypeMap[instance->RTTI_GetType()];
}
else
{
firstOfTypeMap[instance->RTTI_GetType()] = instance;
}
componentEditor->AddInstance(instance, aggregateInstance, nullptr);
// Try and get the underlying SC entity
AZStd::any* userData{};
GraphCanvas::NodeRequestBus::EventResult(userData, instance->GetEntityId(), &GraphCanvas::NodeRequests::GetUserData);
AZ::EntityId scriptCanvasId = userData && userData->is<AZ::EntityId>() ? *AZStd::any_cast<AZ::EntityId>(userData) : AZ::EntityId();
if (scriptCanvasId.IsValid())
{
entitySet.insert(scriptCanvasId);
}
else
{
entitySet.insert(instance->GetEntityId());
}
}
}
const AZStd::string GetMethod(AZ::Component* component)
{
auto classData = AzToolsFramework::GetComponentClassData(component);
if (!classData)
{
return "";
}
if (!classData->m_azRtti->IsTypeOf<ScriptCanvas::Nodes::Core::Method>())
{
return "";
}
ScriptCanvas::Nodes::Core::Method* method = azrtti_cast<ScriptCanvas::Nodes::Core::Method*>(component);
return method->GetMethodClassName() + method->GetName();
}
AZStd::string GetEBusEventHandlerString(const AZ::EntityId& entityId,
AZ::Component* component)
{
auto classData = AzToolsFramework::GetComponentClassData(component);
if (!classData)
{
return "";
}
if (!classData->m_azRtti->IsTypeOf<ScriptCanvas::Nodes::Core::EBusEventHandler>())
{
return "";
}
ScriptCanvas::Nodes::Core::EBusEventHandler* eventHandler = azrtti_cast<ScriptCanvas::Nodes::Core::EBusEventHandler*>(component);
// IMPORTANT: A wrapped node will have an event name. NOT a wrapper node.
AZStd::string eventName;
ScriptCanvasEditor::EBusHandlerEventNodeDescriptorRequestBus::EventResult(eventName, entityId, &ScriptCanvasEditor::EBusHandlerEventNodeDescriptorRequests::GetEventName);
AZStd::string result = eventHandler->GetEBusName() + eventName;
return result;
}
// Returns a set of unique display component instances
AZStd::list<AZ::Component*> GetVisibleGcInstances(const AZ::EntityId& entityId)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
AZStd::list<AZ::Component*> result;
GraphCanvas::GraphCanvasPropertyBus::EnumerateHandlersId(entityId,
[&result](GraphCanvas::GraphCanvasPropertyInterface* propertyInterface) -> bool
{
AZ::Component* component = propertyInterface->GetPropertyComponent();
if (AzToolsFramework::ShouldInspectorShowComponent(component))
{
result.push_back(component);
}
// Continue enumeration.
return true;
});
return result;
}
// Returns a set of unique display component instances
AZStd::list<AZ::Component*> GetVisibleScInstances(const AZ::EntityId& entityId)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
// GraphCanvas entityId -> scriptCanvasEntity
AZStd::any* userData {};
GraphCanvas::NodeRequestBus::EventResult(userData, entityId, &GraphCanvas::NodeRequests::GetUserData);
AZ::EntityId scriptCanvasId = userData && userData->is<AZ::EntityId>() ? *AZStd::any_cast<AZ::EntityId>(userData) : AZ::EntityId();
if (!scriptCanvasId.IsValid())
{
return AZStd::list<AZ::Component*>();
}
AZ::Entity* scriptCanvasEntity = AzToolsFramework::GetEntityById(scriptCanvasId);
if (!scriptCanvasEntity)
{
return AZStd::list<AZ::Component*>();
}
// scriptCanvasEntity -> ScriptCanvas::Node
AZStd::list<AZ::Component*> result;
auto components = AZ::EntityUtils::FindDerivedComponents<ScriptCanvas::Node>(scriptCanvasEntity);
for (auto component : components)
{
if (AzToolsFramework::ShouldInspectorShowComponent(component))
{
result.push_back(component);
}
}
return result;
}
void MoveInstances(const AZStd::string& position,
const AZ::EntityId& entityId,
AZStd::list<AZ::Component*>& gcInstances,
AZStd::list<AZ::Component*>& scInstances,
StringToInstanceMap& instancesToDisplay)
{
GRAPH_CANVAS_PROFILE_FUNCTION();
if (position.empty() ||
(gcInstances.empty() && scInstances.empty()))
{
return;
}
auto& entry = instancesToDisplay[position];
if (!entry.m_gcEntityId.IsValid())
{
entry.m_gcEntityId = entityId;
}
if (!gcInstances.empty())
{
entry.m_gcInstances.splice(entry.m_gcInstances.end(), gcInstances);
}
if (!scInstances.empty())
{
entry.m_scInstances.splice(entry.m_scInstances.end(), scInstances);
}
}
AZStd::string GetKeyForInstancesToDisplay(const AZ::EntityId& entityId,
const AZStd::list<AZ::Component*>& gcInstances,
const AZStd::list<AZ::Component*>& scInstances)
{
GRAPH_CANVAS_PROFILE_FUNCTION();
AZStd::string result;
if (!scInstances.empty())
{
auto component = scInstances.front();
result = GetMethod(component);
if (!result.empty())
{
return result;
}
result = GetEBusEventHandlerString(entityId, component);
if (!result.empty())
{
return result;
}
return component->RTTI_GetType().ToString<AZStd::string>();
}
if (!gcInstances.empty())
{
return gcInstances.front()->RTTI_GetType().ToString<AZStd::string>();
}
return result;
}
void GetInstancesToDisplay(const AZStd::vector<AZ::EntityId>& selectedEntityIds,
StringToInstanceMap& instancesToDisplay)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
for (auto& entityId : selectedEntityIds)
{
GRAPH_CANVAS_DETAILED_PROFILE_SCOPE("GetInstancesToDisplay::InnerLoop");
AZStd::list<AZ::Component*> gcInstances = GetVisibleGcInstances(entityId);
AZStd::list<AZ::Component*> scInstances = GetVisibleScInstances(entityId);
AZStd::string position = GetKeyForInstancesToDisplay(entityId, gcInstances, scInstances);
MoveInstances(position, entityId, gcInstances, scInstances, instancesToDisplay);
}
}
}
namespace ScriptCanvasEditor
{
namespace Widget
{
PropertyGrid::PropertyGrid(QWidget* parent /*= nullptr*/, const char* name /*= "Properties"*/)
: AzQtComponents::StyledDockWidget(parent)
{
// This is used for styling.
setObjectName("PropertyGrid");
m_spacer = new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding);
setWindowTitle(name);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_scrollArea = new QScrollArea(this);
m_scrollArea->setWidgetResizable(true);
m_scrollArea->setSizePolicy(QSizePolicy::Policy::Ignored, QSizePolicy::Policy::Ignored);
m_host = new QWidget;
m_host->setLayout(new QVBoxLayout());
m_scrollArea->setWidget(m_host);
setWidget(m_scrollArea);
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
UpdateContents(AZStd::vector<AZ::EntityId>());
PropertyGridRequestBus::Handler::BusConnect();
}
PropertyGrid::~PropertyGrid()
{
}
void PropertyGrid::ClearSelection()
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
for (auto& componentEditor : m_componentEditors)
{
// Component editor deletion needs to be deferred until the next frame
// as ClearSelection can be called when a slot is removed via the Reflected
// therefore causing the reflected property editor to be deleted while it is still
// in the callstack
// Deleting a node will cause the selection change event to be fired from the GraphCanvas Scene which leads to the selection being cleared
// Furthermore that change queues a property editor refresh for next frame, which if the node contained an EntityId slot it attempts to access
// the node address which has been deleted.
// Therefore the property editor property modification refresh level is set to none to prevent a refresh before it gets deleted
componentEditor->GetPropertyEditor()->CancelQueuedRefresh();
componentEditor->setVisible(false);
componentEditor.release()->deleteLater();
}
m_componentEditors.clear();
ScriptCanvas::EndpointNotificationBus::MultiHandler::BusDisconnect();
ScriptCanvas::NodeNotificationsBus::MultiHandler::BusDisconnect();
}
void PropertyGrid::DisplayInstances(const InstancesToDisplay& instances)
{
GRAPH_CANVAS_PROFILE_FUNCTION();
if (instances.m_gcInstances.empty() &&
instances.m_scInstances.empty())
{
return;
}
AzToolsFramework::ComponentEditor* componentEditor = CreateComponentEditor();
AZ::Component* firstGcInstance = !instances.m_gcInstances.empty() ? instances.m_gcInstances.front() : nullptr;
AZ::Component* firstScInstance = !instances.m_scInstances.empty() ? instances.m_scInstances.front() : nullptr;
AZStd::unordered_map<AZ::TypeId, AZ::Component*> firstOfTypeMap;
AZStd::unordered_set<AZ::EntityId> entitySet;
// This adds all the component instances to the component editor widget and aggregates them based on the component types
AddInstancesToComponentEditor(componentEditor, instances.m_gcInstances, firstOfTypeMap, entitySet);
AddInstancesToComponentEditor(componentEditor, instances.m_scInstances, firstOfTypeMap, entitySet);
// Set the title.
// This MUST be done AFTER AddInstance() to override the default title.
AZStd::string title = GetTitle(instances.m_gcEntityId, firstScInstance ? firstScInstance : firstGcInstance);
// Use the number of unique entities to determine the number of selected entities for this component editor
if (entitySet.size() > 1)
{
title += AZStd::string::format(" (%zu Selected)", entitySet.size());
}
componentEditor->GetHeader()->SetTitle(title.c_str());
{
GRAPH_CANVAS_DETAILED_PROFILE_SCOPE("PropertyGrid::DisplayInstance::RefreshEditor");
// Refresh editor
componentEditor->AddNotifications();
componentEditor->SetExpanded(true);
componentEditor->InvalidateAll();
}
// hiding the icon on the header for Preview
componentEditor->GetHeader()->SetIcon(QIcon());
componentEditor->show();
}
ScriptCanvas::ScriptCanvasId PropertyGrid::GetScriptCanvasId(AZ::Component* component)
{
ScriptCanvas::ScriptCanvasId executionId;
if (const ScriptCanvas::Node* node = AZ::EntityUtils::FindFirstDerivedComponent<ScriptCanvas::Node>(component->GetEntity()))
{
executionId = node->GetOwningScriptCanvasId();
}
else
{
GeneralRequestBus::BroadcastResult(executionId, &GeneralRequests::GetActiveScriptCanvasId);
if (!executionId.IsValid())
{
AZ::EntityId graphCanvasGraphId;
// GraphCanvas Node
GraphCanvas::SceneMemberRequestBus::EventResult(graphCanvasGraphId, component->GetEntityId(), &GraphCanvas::SceneMemberRequests::GetScene);
GeneralRequestBus::BroadcastResult(executionId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
}
}
return executionId;
}
AzToolsFramework::ComponentEditor* PropertyGrid::CreateComponentEditor()
{
GRAPH_CANVAS_PROFILE_FUNCTION();
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
{
GRAPH_CANVAS_DETAILED_PROFILE_SCOPE("CreateComponentEditor::ComponentConstruction");
m_componentEditors.push_back(AZStd::make_unique<AzToolsFramework::ComponentEditor>(serializeContext, this, this));
}
AzToolsFramework::ComponentEditor* componentEditor = m_componentEditors.back().get();
{
GRAPH_CANVAS_DETAILED_PROFILE_SCOPE("CreateComponentEditor::ComponentConfiguration");
componentEditor->GetHeader()->SetHasContextMenu(false);
componentEditor->GetPropertyEditor()->SetHideRootProperties(false);
componentEditor->GetPropertyEditor()->SetAutoResizeLabels(true);
connect(componentEditor, &AzToolsFramework::ComponentEditor::OnExpansionContractionDone, this, [this]()
{
m_host->layout()->update();
m_host->layout()->activate();
});
}
{
GRAPH_CANVAS_DETAILED_PROFILE_SCOPE("CreateComponentEditor::SpacerUpdates");
//move spacer to bottom of editors
m_host->layout()->removeItem(m_spacer);
m_host->layout()->addWidget(componentEditor);
m_host->layout()->addItem(m_spacer);
m_host->layout()->update();
}
return componentEditor;
}
void PropertyGrid::SetSelection(const AZStd::vector<AZ::EntityId>& selectedEntityIds)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
ClearSelection();
UpdateContents(selectedEntityIds);
RefreshPropertyGrid();
}
void PropertyGrid::OnNodeUpdate(const AZ::EntityId&)
{
RefreshPropertyGrid();
}
void PropertyGrid::BeforePropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode)
{
GeneralRequestBus::Broadcast(&GeneralRequests::PushPreventUndoStateUpdate);
}
void PropertyGrid::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
GeneralRequestBus::Broadcast(&GeneralRequests::PopPreventUndoStateUpdate);
AzToolsFramework::InstanceDataNode* componentNode = pNode;
do
{
auto* componentClassData = componentNode->GetClassMetadata();
if (componentClassData && componentClassData->m_azRtti && componentClassData->m_azRtti->IsTypeOf(azrtti_typeid<AZ::Component>()))
{
break;
}
} while (componentNode = componentNode->GetParent());
if (!componentNode)
{
AZ_Warning("Script Canvas", false, "Failed to locate component data associated with the script canvas property. Unable to mark parent Entity as dirty.");
return;
}
// Only need one instance to lookup the SceneId in-order to record the undo state
const size_t firstInstanceIdx = 0;
if (componentNode->GetNumInstances())
{
AZ::SerializeContext* context = componentNode->GetSerializeContext();
AZ::Component* componentInstance = context->Cast<AZ::Component*>(componentNode->GetInstance(firstInstanceIdx), componentNode->GetClassMetadata()->m_typeId);
if (componentInstance && componentInstance->GetEntity())
{
ScriptCanvas::ScriptCanvasId scriptCanvasId = GetScriptCanvasId(componentInstance);
GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, scriptCanvasId);
}
}
}
void PropertyGrid::SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode)
{
}
void PropertyGrid::SetPropertyEditingComplete([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode)
{
}
void PropertyGrid::RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode* node, const QPoint& point)
{
PropertyGridContextMenu contextMenu(node);
if (!contextMenu.actions().empty())
{
contextMenu.exec(point);
}
}
void PropertyGrid::OnSlotDisplayTypeChanged(const ScriptCanvas::SlotId& slotId, [[maybe_unused]] const ScriptCanvas::Data::Type& slotType)
{
const AZ::EntityId* nodeId = ScriptCanvas::NodeNotificationsBus::GetCurrentBusId();
if (nodeId)
{
ScriptCanvas::Endpoint scriptCanvasEndpoint((*nodeId), slotId);
UpdateEndpointVisibility(scriptCanvasEndpoint);
}
}
void PropertyGrid::RefreshPropertyGrid()
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
for (auto& componentEditor : m_componentEditors)
{
if (componentEditor->isVisible())
{
componentEditor->QueuePropertyEditorInvalidation(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_Values);
}
else
{
break;
}
}
}
void PropertyGrid::RebuildPropertyGrid()
{
for (auto& componentEditor : m_componentEditors)
{
if (componentEditor->isVisible())
{
componentEditor->QueuePropertyEditorInvalidation(AzToolsFramework::PropertyModificationRefreshLevel::Refresh_EntireTree);
}
else
{
break;
}
}
}
void PropertyGrid::SetVisibility(const AZStd::vector<AZ::EntityId>& selectedEntityIds)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
// Set the visibility and connect for changes.
for (auto& gcNodeEntityId : selectedEntityIds)
{
// GC node -> SC node.
AZStd::any* nodeUserData = nullptr;
GraphCanvas::NodeRequestBus::EventResult(nodeUserData, gcNodeEntityId, &GraphCanvas::NodeRequests::GetUserData);
AZ::EntityId scNodeEntityId = nodeUserData && nodeUserData->is<AZ::EntityId>() ? *AZStd::any_cast<AZ::EntityId>(nodeUserData) : AZ::EntityId();
AZ::Entity* nodeEntity{};
AZ::ComponentApplicationBus::BroadcastResult(nodeEntity, &AZ::ComponentApplicationRequests::FindEntity, scNodeEntityId);
auto node = nodeEntity ? AZ::EntityUtils::FindFirstDerivedComponent<ScriptCanvas::Node>(nodeEntity) : nullptr;
if (!node)
{
continue;
}
ScriptCanvas::NodeNotificationsBus::MultiHandler::BusConnect(node->GetEntityId());
AZStd::vector<AZ::EntityId> gcSlotEntityIds;
GraphCanvas::NodeRequestBus::EventResult(gcSlotEntityIds, gcNodeEntityId, &GraphCanvas::NodeRequests::GetSlotIds);
for (auto& gcSlotEntityId : gcSlotEntityIds)
{
// GC slot -> SC slot.
AZStd::any* slotUserData = nullptr;
GraphCanvas::SlotRequestBus::EventResult(slotUserData, gcSlotEntityId, &GraphCanvas::SlotRequests::GetUserData);
ScriptCanvas::SlotId scSlotId = slotUserData && slotUserData->is<ScriptCanvas::SlotId>() ? *AZStd::any_cast<ScriptCanvas::SlotId>(slotUserData) : ScriptCanvas::SlotId();
ScriptCanvas::Slot* slot = node->GetSlot(scSlotId);
if (!slot || slot->GetDescriptor() != ScriptCanvas::SlotDescriptors::DataIn())
{
continue;
}
slot->UpdateDatumVisibility();
// Connect to get notified of changes.
ScriptCanvas::EndpointNotificationBus::MultiHandler::BusConnect(ScriptCanvas::Endpoint(scNodeEntityId, scSlotId));
}
}
}
void PropertyGrid::UpdateContents(const AZStd::vector<AZ::EntityId>& selectedEntityIds)
{
GRAPH_CANVAS_DETAILED_PROFILE_FUNCTION();
if (!selectedEntityIds.empty())
{
// Build up components to display
StringToInstanceMap instanceMap;
GetInstancesToDisplay(selectedEntityIds, instanceMap);
SetVisibility(selectedEntityIds);
for (auto& pair : instanceMap)
{
GRAPH_CANVAS_DETAILED_PROFILE_SCOPE("PropertyGrid::UpdateContents::InstanceMapLoop");
DisplayInstances(pair.second);
}
}
}
void PropertyGrid::OnEndpointConnected([[maybe_unused]] const ScriptCanvas::Endpoint& targetEndpoint)
{
const ScriptCanvas::Endpoint* sourceEndpoint = ScriptCanvas::EndpointNotificationBus::GetCurrentBusId();
if (sourceEndpoint)
{
UpdateEndpointVisibility(*sourceEndpoint);
}
}
void PropertyGrid::OnEndpointDisconnected([[maybe_unused]] const ScriptCanvas::Endpoint& targetEndpoint)
{
const ScriptCanvas::Endpoint* sourceEndpoint = ScriptCanvas::EndpointNotificationBus::GetCurrentBusId();
if (sourceEndpoint)
{
UpdateEndpointVisibility(*sourceEndpoint);
}
}
void PropertyGrid::OnEndpointConvertedToValue()
{
const ScriptCanvas::Endpoint* sourceEndpoint = ScriptCanvas::EndpointNotificationBus::GetCurrentBusId();
if (sourceEndpoint)
{
UpdateEndpointVisibility(*sourceEndpoint);
}
}
void PropertyGrid::OnEndpointConvertedToReference()
{
const ScriptCanvas::Endpoint* sourceEndpoint = ScriptCanvas::EndpointNotificationBus::GetCurrentBusId();
if (sourceEndpoint)
{
UpdateEndpointVisibility(*sourceEndpoint);
}
}
void PropertyGrid::UpdateEndpointVisibility(const ScriptCanvas::Endpoint& endpoint)
{
ScriptCanvas::Slot* slot = nullptr;
ScriptCanvas::NodeRequestBus::EventResult(slot, endpoint.GetNodeId(), &ScriptCanvas::NodeRequests::GetSlot, endpoint.GetSlotId());
if (slot)
{
slot->UpdateDatumVisibility();
RebuildPropertyGrid();
}
}
#include <Editor/View/Widgets/moc_PropertyGrid.cpp>
}
}
@@ -0,0 +1,123 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Asset/AssetCommon.h>
#include <Core/Node.h>
#include <ScriptCanvas/Core/GraphBus.h>
#include "PropertyGridBus.h"
#endif
class QSpacerItem;
class QScrollArea;
namespace AzToolsFramework
{
class PropertiesWidget;
class EntityPropertyEditor;
namespace UndoSystem
{
class URSequencePoint;
}
class ComponentEditor;
}
namespace ScriptCanvasEditor
{
namespace Widget
{
class PropertyGrid
: public AzQtComponents::StyledDockWidget
, public AzToolsFramework::IPropertyEditorNotify
, public PropertyGridRequestBus::Handler
, public ScriptCanvas::EndpointNotificationBus::MultiHandler
, public ScriptCanvas::NodeNotificationsBus::MultiHandler
{
Q_OBJECT
public:
PropertyGrid(QWidget* parent = nullptr, const char* name = "Properties");
~PropertyGrid() override;
// AzToolsFramework::IPropertyEditorNotify
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void SetPropertyEditingActive(AzToolsFramework::InstanceDataNode* pNode) override;
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
void RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode* node, const QPoint& point) override;
/////
// NodeNotificationsBus
void OnSlotDisplayTypeChanged(const ScriptCanvas::SlotId& slotId, const ScriptCanvas::Data::Type& slotType) override;
////
// PropertyGridRequestHandler
void RefreshPropertyGrid() override;
void RebuildPropertyGrid() override;
void SetSelection(const AZStd::vector<AZ::EntityId>& selectedEntityIds) override;
void ClearSelection() override;
////
void SealUndoStack() override {};
void OnNodeUpdate(const AZ::EntityId&);
struct InstancesToDisplay
{
//! This is ONLY used to get the title of the node.
//! This entity ISN'T necessarily the owner of m_gcInstances and m_scInstances.
AZ::EntityId m_gcEntityId;
AZStd::list<AZ::Component*> m_gcInstances;
AZStd::list<AZ::Component*> m_scInstances;
};
private slots:
void UpdateContents(const AZStd::vector<AZ::EntityId>& selectedEntityIds);
// ScriptCanvas::EndpointNotificationBus::MultiHandler
void OnEndpointConnected(const ScriptCanvas::Endpoint& targetEndpoint) override;
void OnEndpointDisconnected(const ScriptCanvas::Endpoint& targetEndpoint) override;
void OnEndpointConvertedToValue() override;
void OnEndpointConvertedToReference() override;
////////////////////////////
void UpdateEndpointVisibility(const ScriptCanvas::Endpoint& endpoint);
private:
void SetVisibility(const AZStd::vector<AZ::EntityId>& selectedEntityIds);
void DisplayInstances(const InstancesToDisplay& instances);
ScriptCanvas::ScriptCanvasId GetScriptCanvasId(AZ::Component* component);
AzToolsFramework::ComponentEditor* CreateComponentEditor();
AZStd::vector<AZStd::unique_ptr<AzToolsFramework::ComponentEditor> > m_componentEditors;
// the spacer's job is to make sure that its always at the end of the list of components.
QSpacerItem* m_spacer = nullptr;
QScrollArea* m_scrollArea = nullptr;
QWidget* m_host = nullptr;
};
}
}
@@ -0,0 +1,33 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzCore/Component/EntityId.h>
namespace ScriptCanvasEditor
{
class PropertyGridRequests : public AZ::EBusTraits
{
public:
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
virtual void RefreshPropertyGrid() = 0;
virtual void RebuildPropertyGrid() = 0;
virtual void SetSelection(const AZStd::vector<AZ::EntityId>& selectedEntityIds) = 0;
virtual void ClearSelection() = 0;
};
using PropertyGridRequestBus = AZ::EBus<PropertyGridRequests>;
}
@@ -0,0 +1,154 @@
/*
* 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 "PropertyGrid.h"
#include <QAction>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <Editor/View/Widgets/PropertyGridContextMenu.h>
namespace
{
struct SlotInfo
{
AZ::EntityId m_id;
QString m_name;
bool m_isSetter; // false means "is getter".
bool m_isVisible;
};
using SlotInfoList = AZStd::list< SlotInfo >;
bool IsGraphCanvasActive()
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
ScriptCanvasEditor::GeneralRequestBus::BroadcastResult(scriptCanvasId, &ScriptCanvasEditor::GeneralRequests::GetActiveScriptCanvasId);
AZ::EntityId graphCanvasGraphId;
ScriptCanvasEditor::GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &ScriptCanvasEditor::GeneralRequests::GetActiveGraphCanvasGraphId);
return (scriptCanvasId.IsValid() &&
graphCanvasGraphId.IsValid());
}
AZ::EntityId GetEntityId(AzToolsFramework::InstanceDataNode* node)
{
while (node)
{
if ((node->GetClassMetadata()) && (node->GetClassMetadata()->m_azRtti))
{
if (node->GetClassMetadata()->m_azRtti->IsTypeOf(AZ::Component::RTTI_Type()))
{
return static_cast<AZ::Component*>(node->GetInstance(0))->GetEntityId();
}
}
node = node->GetParent();
}
return AZ::EntityId();
}
SlotInfoList BuildSlotList(const AZ::EntityId& entityId)
{
SlotInfoList slotsList;
AZStd::vector<AZ::EntityId> slotIds;
GraphCanvas::NodeRequestBus::EventResult(slotIds, entityId, &GraphCanvas::NodeRequests::GetSlotIds);
for (AZ::EntityId slotId : slotIds)
{
GraphCanvas::SlotType type(GraphCanvas::SlotTypes::Invalid);
GraphCanvas::SlotRequestBus::EventResult(type, slotId, &GraphCanvas::SlotRequests::GetSlotType);
if (type != GraphCanvas::SlotTypes::DataSlot)
{
// This ISN'T a setter or getter slot.
// Nothing to do.
continue;
}
GraphCanvas::ConnectionType connectionType(GraphCanvas::ConnectionType::CT_None);
GraphCanvas::SlotRequestBus::EventResult(connectionType, slotId, &GraphCanvas::SlotRequests::GetConnectionType);
AZStd::string name;
GraphCanvas::SlotRequestBus::EventResult(name, slotId, &GraphCanvas::SlotRequests::GetName);
bool isVisible = false;
GraphCanvas::VisualRequestBus::EventResult(isVisible, slotId, &GraphCanvas::VisualRequests::IsVisible);
slotsList.push_back({slotId,
name.c_str(),
(connectionType == GraphCanvas::ConnectionType::CT_Output),
isVisible});
}
return slotsList;
}
void AddVisibilityActions(ScriptCanvasEditor::Widget::PropertyGridContextMenu* rootMenu,
const SlotInfoList& slotsList)
{
for (auto& slot : slotsList)
{
QString title(QString("%1 : %2").arg(slot.m_name,
slot.m_isSetter ? "setter" : "getter"));
QAction* action = new QAction(title, rootMenu);
action->setCheckable(true);
action->setChecked(slot.m_isVisible);
QObject::connect(action,
&QAction::triggered,
[slot]([[maybe_unused]] bool checked)
{
// slot.m_isVisible is the current state, and "checked" is the new state.
AZ_Assert(checked != slot.m_isVisible, "Visibility out of synch");
GraphCanvas::VisualRequestBus::Event(slot.m_id, &GraphCanvas::VisualRequests::SetVisible, !slot.m_isVisible);
});
rootMenu->addAction(action);
}
}
} // anonymous namespace.
namespace ScriptCanvasEditor
{
namespace Widget
{
PropertyGridContextMenu::PropertyGridContextMenu(AzToolsFramework::InstanceDataNode* node)
: QMenu()
{
if (!IsGraphCanvasActive())
{
// Nothing active.
return;
}
AZ::EntityId graphCanvasNodeId = GetEntityId(node);
if (!graphCanvasNodeId.IsValid())
{
// Nothing to do.
return;
}
AddVisibilityActions(this, BuildSlotList(graphCanvasNodeId));
}
#include <Editor/View/Widgets/moc_PropertyGridContextMenu.cpp>
}
}
@@ -0,0 +1,38 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QMenu>
#endif
namespace AzToolsFramework
{
class InstanceDataNode;
}
namespace ScriptCanvasEditor
{
namespace Widget
{
class PropertyGridContextMenu
: public QMenu
{
Q_OBJECT
public:
PropertyGridContextMenu(AzToolsFramework::InstanceDataNode* node);
};
}
}
@@ -0,0 +1,684 @@
/*
* 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 <QLineEdit>
#include <QMenu>
#include <QSignalBlocker>
#include <QScrollBar>
#include <QBoxLayout>
#include <QPainter>
#include <QEvent>
#include <QCoreApplication>
#include <QCompleter>
#include <QHeaderView>
#include <QScopedValueRollback>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetEditor/AssetEditorUtils.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <ScriptEvents/ScriptEventsAsset.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h>
#include <GraphCanvas/Widgets/NodePalette/NodePaletteWidget.h>
#include <GraphCanvas/Widgets/NodePalette/NodePaletteTreeView.h>
#include <Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h>
#include <Editor/View/Widgets/ui_ScriptCanvasNodePaletteToolbar.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/Nodes/NodeUtils.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.h>
#include <Editor/View/Widgets/NodePalette/FunctionNodePaletteTreeItemTypes.h>
#include <Editor/View/Widgets/NodePalette/ScriptEventsNodePaletteTreeItemTypes.h>
#include <Editor/View/Widgets/NodePalette/GeneralNodePaletteTreeItemTypes.h>
#include <Editor/View/Widgets/NodePalette/SpecializedNodePaletteTreeItemTypes.h>
#include <Editor/View/Widgets/NodePalette/VariableNodePaletteTreeItemTypes.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <Editor/Assets/ScriptCanvasAssetHelpers.h>
#include <Editor/Components/IconComponent.h>
#include <Editor/Include/ScriptCanvas/Bus/RequestBus.h>
#include <Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h>
#include <Editor/Settings.h>
#include <Editor/Translation/TranslationHelper.h>
#include <ScriptCanvas/Asset/RuntimeAsset.h>
#include <ScriptCanvas/Data/DataRegistry.h>
#include <ScriptCanvas/Utils/NodeUtils.h>
#include <ScriptCanvas/Core/Attributes.h>
#include <ScriptCanvas/Libraries/Entity/EntityRef.h>
#include <ScriptCanvas/Libraries/Libraries.h>
#include <ScriptCanvas/Libraries/Core/GetVariable.h>
#include <ScriptCanvas/Libraries/Core/Method.h>
#include <ScriptCanvas/Libraries/Core/SetVariable.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
namespace ScriptCanvasEditor
{
namespace Widget
{
//////////////////////
// NodePaletteWidget
//////////////////////
GraphCanvas::NodePaletteTreeItem* NodePaletteWidget::ExternalCreateNodePaletteRoot(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel)
{
ScriptCanvasRootPaletteTreeItem* root = aznew ScriptCanvasRootPaletteTreeItem(nodePaletteModel, assetModel);
{
GraphCanvas::NodePaletteTreeItem* utilitiesRoot = root->GetCategoryNode("Utilities");
GraphCanvas::NodePaletteTreeItem* variablesRoot = root->CreateChildNode<LocalVariablesListNodePaletteTreeItem>("Variables");
root->RegisterCategoryNode(variablesRoot, "Variables");
// We always want to keep thede around as place holders
GraphCanvas::NodePaletteTreeItem* customEventRoot = root->GetCategoryNode("Script Events");
customEventRoot->SetAllowPruneOnEmpty(false);
GraphCanvas::NodePaletteTreeItem* globalFunctionRoot = root->GetCategoryNode("Global Functions");
globalFunctionRoot->SetAllowPruneOnEmpty(false);
}
const NodePaletteModel::NodePaletteRegistry& nodeRegistry = nodePaletteModel.GetNodeRegistry();
for (const auto& registryPair : nodeRegistry)
{
const NodePaletteModelInformation* modelInformation = registryPair.second;
GraphCanvas::GraphCanvasTreeItem* parentItem = root->GetCategoryNode(modelInformation->m_categoryPath.c_str());
GraphCanvas::NodePaletteTreeItem* createdItem = nullptr;
if (auto customModelInformation = azrtti_cast<const CustomNodeModelInformation*>(modelInformation))
{
createdItem = parentItem->CreateChildNode<CustomNodePaletteTreeItem>(customModelInformation->m_typeId, customModelInformation->m_displayName);
createdItem->SetToolTip(QString(customModelInformation->m_toolTip.c_str()));
}
else if (auto methodNodeModelInformation = azrtti_cast<const MethodNodeModelInformation*>(modelInformation))
{
createdItem = parentItem->CreateChildNode<ClassMethodEventPaletteTreeItem>(methodNodeModelInformation->m_classMethod, methodNodeModelInformation->m_metehodName);
}
else if (auto ebusHandlerNodeModelInformation = azrtti_cast<const EBusHandlerNodeModelInformation*>(modelInformation))
{
if (!azrtti_istypeof<const ScriptEventHandlerNodeModelInformation*>(ebusHandlerNodeModelInformation))
{
createdItem = parentItem->CreateChildNode<EBusHandleEventPaletteTreeItem>(ebusHandlerNodeModelInformation->m_busName, ebusHandlerNodeModelInformation->m_eventName, ebusHandlerNodeModelInformation->m_busId, ebusHandlerNodeModelInformation->m_eventId);
}
}
else if (auto ebusSenderNodeModelInformation = azrtti_cast<const EBusSenderNodeModelInformation*>(modelInformation))
{
if (!azrtti_istypeof<const ScriptEventSenderNodeModelInformation*>(ebusSenderNodeModelInformation))
{
createdItem = parentItem->CreateChildNode<EBusSendEventPaletteTreeItem>(ebusSenderNodeModelInformation->m_busName, ebusSenderNodeModelInformation->m_eventName, ebusSenderNodeModelInformation->m_busId, ebusSenderNodeModelInformation->m_eventId);
}
}
if (createdItem)
{
modelInformation->PopulateTreeItem((*createdItem));
}
}
root->PruneEmptyNodes();
return root;
}
////////////////////////////////////
// ScriptCanvasRootPaletteTreeItem
////////////////////////////////////
ScriptCanvasRootPaletteTreeItem::ScriptCanvasRootPaletteTreeItem(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel)
: GraphCanvas::NodePaletteTreeItem("root", ScriptCanvasEditor::AssetEditorId)
, m_nodePaletteModel(nodePaletteModel)
, m_assetModel(assetModel)
, m_categorizer(nodePaletteModel)
, m_isFunctionGraphActive(false)
{
if (m_assetModel)
{
TraverseTree();
{
auto connection = QObject::connect(m_assetModel, &QAbstractItemModel::rowsInserted, [this](const QModelIndex& parentIndex, int first, int last) { this->OnRowsInserted(parentIndex, first, last); });
m_lambdaConnections.emplace_back(connection);
}
{
auto connection = QObject::connect(m_assetModel, &QAbstractItemModel::rowsAboutToBeRemoved, [this](const QModelIndex& parentIndex, int first, int last) { this->OnRowsAboutToBeRemoved(parentIndex, first, last); });
m_lambdaConnections.emplace_back(connection);
}
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
}
}
ScriptCanvasRootPaletteTreeItem::~ScriptCanvasRootPaletteTreeItem()
{
for (auto connection : m_lambdaConnections)
{
QObject::disconnect(connection);
}
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
}
void ScriptCanvasRootPaletteTreeItem::RegisterCategoryNode(GraphCanvas::GraphCanvasTreeItem* treeItem, const char* subCategory, GraphCanvas::NodePaletteTreeItem* parentRoot)
{
if (parentRoot == nullptr)
{
parentRoot = this;
}
m_categorizer.RegisterCategoryNode(treeItem, subCategory, parentRoot);
}
// Given a category path (e.g. "My/Category") and a parent node, creates the necessary intermediate
// nodes under the given parent and returns the leaf tree item under the given category path.
GraphCanvas::NodePaletteTreeItem* ScriptCanvasRootPaletteTreeItem::GetCategoryNode(const char* categoryPath, GraphCanvas::NodePaletteTreeItem* parentRoot)
{
if (parentRoot)
{
return static_cast<GraphCanvas::NodePaletteTreeItem*>(m_categorizer.GetCategoryNode(categoryPath, parentRoot));
}
else
{
return static_cast<GraphCanvas::NodePaletteTreeItem*>(m_categorizer.GetCategoryNode(categoryPath, this));
}
}
void ScriptCanvasRootPaletteTreeItem::PruneEmptyNodes()
{
m_categorizer.PruneEmptyNodes();
}
void ScriptCanvasRootPaletteTreeItem::SetActiveScriptCanvasId(const ScriptCanvas::ScriptCanvasId& scriptCanvasId)
{
ScriptCanvas::RuntimeRequestBus::EventResult(m_previousAssetId, scriptCanvasId, &ScriptCanvas::RuntimeRequests::GetAssetId);
m_isFunctionGraphActive = false;
EditorGraphRequestBus::EventResult(m_isFunctionGraphActive, scriptCanvasId, &EditorGraphRequests::IsFunctionGraph);
for (auto functionTreePair : m_globalFunctionTreeItems)
{
functionTreePair.second->SetEnabled(!m_isFunctionGraphActive);
}
}
void ScriptCanvasRootPaletteTreeItem::OnRowsInserted(const QModelIndex& parentIndex, int first, int last)
{
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = m_assetModel->index(i, 0, parentIndex);
QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessAsset(entry);
}
}
void ScriptCanvasRootPaletteTreeItem::OnRowsAboutToBeRemoved(const QModelIndex& parentIndex, int first, int last)
{
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = m_assetModel->index(first, 0, parentIndex);
QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex);
const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
const AZ::Data::AssetId& assetId = productEntry->GetAssetId();
auto scriptEventElementIter = m_scriptEventElementTreeItems.find(assetId);
if (scriptEventElementIter != m_scriptEventElementTreeItems.end())
{
scriptEventElementIter->second->DetachItem();
delete scriptEventElementIter->second;
m_scriptEventElementTreeItems.erase(scriptEventElementIter);
}
auto globalFunctionElementIter = m_globalFunctionTreeItems.find(assetId.m_guid);
if (globalFunctionElementIter != m_globalFunctionTreeItems.end())
{
globalFunctionElementIter->second->DetachItem();
delete globalFunctionElementIter->second;
m_globalFunctionTreeItems.erase(globalFunctionElementIter);
}
}
}
PruneEmptyNodes();
}
void ScriptCanvasRootPaletteTreeItem::TraverseTree(QModelIndex index)
{
QModelIndex sourceIndex = m_assetModel->mapToSource(index);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessAsset(entry);
int rowCount = m_assetModel->rowCount(index);
for (int i = 0; i < rowCount; ++i)
{
QModelIndex nextIndex = m_assetModel->index(i, 0, index);
TraverseTree(nextIndex);
}
}
void ScriptCanvasRootPaletteTreeItem::ProcessAsset(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
if (entry)
{
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = static_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
if (productEntry->GetAssetType() == azrtti_typeid<ScriptCanvas::RuntimeFunctionAsset>() ||
productEntry->GetAssetType() == azrtti_typeid<ScriptEvents::ScriptEventsAsset>())
{
const AZ::Data::AssetId& assetId = productEntry->GetAssetId();
auto elementIter = m_globalFunctionTreeItems.find(assetId.m_guid);
if (elementIter == m_globalFunctionTreeItems.end())
{
RequestAssetLoad(assetId, productEntry->GetAssetType());
}
}
}
}
}
void ScriptCanvasRootPaletteTreeItem::OnCatalogAssetChanged(const AZ::Data::AssetId& /*assetId*/)
{
TraverseTree();
}
void ScriptCanvasRootPaletteTreeItem::OnCatalogAssetAdded(const AZ::Data::AssetId& /*assetId*/)
{
TraverseTree();
}
void ScriptCanvasRootPaletteTreeItem::OnCatalogAssetRemoved(const AZ::Data::AssetId& /*assetId*/, const AZ::Data::AssetInfo& /*assetInfo*/)
{
TraverseTree();
}
void ScriptCanvasRootPaletteTreeItem::RequestAssetLoad(AZ::Data::AssetId assetId, AZ::Data::AssetType assetType)
{
auto entry = AZStd::make_pair(assetId, assetType);
if (m_pendingAssets.find(entry) == m_pendingAssets.end())
{
m_pendingAssets.insert(entry);
AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
AZ::Data::AssetManager::Instance().GetAsset(assetId, assetType, AZ::Data::AssetLoadBehavior::Default);
}
}
void ScriptCanvasRootPaletteTreeItem::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
AZ::Data::AssetId assetId = asset.GetId();
if (m_scriptEventElementTreeItems.find(assetId) != m_scriptEventElementTreeItems.end())
{
return;
}
m_pendingAssets.erase(AZStd::make_pair(assetId, asset.GetType()));
AZ::Data::AssetBus::MultiHandler::BusDisconnect(assetId);
if (asset.GetType() == azrtti_typeid<ScriptEvents::ScriptEventsAsset>())
{
ScriptEvents::ScriptEventsAsset* data = asset.GetAs<ScriptEvents::ScriptEventsAsset>();
if (data)
{
GraphCanvas::NodePaletteTreeItem* categoryRoot = GetCategoryNode(data->m_definition.GetCategory().c_str());
ScriptEventsPaletteTreeItem* treeItem = categoryRoot->CreateChildNode<ScriptEventsPaletteTreeItem>(asset);
if (treeItem)
{
m_scriptEventElementTreeItems[assetId] = treeItem;
}
}
}
else if (asset.GetType() == azrtti_typeid<ScriptCanvas::RuntimeFunctionAsset>())
{
ScriptCanvas::RuntimeFunctionAsset* data = asset.GetAs<ScriptCanvas::RuntimeFunctionAsset>();
AZStd::string rootPath, absolutePath;
AZ::Data::AssetInfo assetInfo = AssetHelpers::GetAssetInfo(assetId, rootPath);
AzFramework::StringFunc::Path::Join(rootPath.c_str(), assetInfo.m_relativePath.c_str(), absolutePath);
AZ::Data::AssetId sourceAssetId;
AZStd::string normPath = absolutePath;
AzFramework::StringFunc::Path::Normalize(normPath);
AZStd::string watchFolder;
bool sourceInfoFound{};
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, normPath.c_str(), assetInfo, watchFolder);
if (!sourceInfoFound)
{
// TODO-LS: report the problem
return;
}
sourceAssetId = assetInfo.m_assetId;
const char* name = assetInfo.m_relativePath.c_str();
AZStd::string prettyName = data->m_runtimeData.m_name;
AZStd::string category = "Global Functions";
AZStd::string relativePath;
if (AzFramework::StringFunc::Path::GetFolderPath(assetInfo.m_relativePath.c_str(), relativePath))
{
AZStd::to_lower(relativePath.begin(), relativePath.end());
const AZStd::string root = "scriptcanvas/functions/";
if (relativePath.starts_with(root))
{
relativePath = relativePath.substr(root.size(), relativePath.size() - root.size());
}
category.append("/");
category.append(relativePath);
}
AZ::Data::AssetId runtimeAssetId = asset.GetId();
GraphCanvas::NodePaletteTreeItem* categoryRoot = GetCategoryNode(category.c_str());
FunctionPaletteTreeItem* treeItem = categoryRoot->CreateChildNode<FunctionPaletteTreeItem>(prettyName.empty() ? name : prettyName.c_str(), sourceAssetId, runtimeAssetId);
if (treeItem)
{
m_globalFunctionTreeItems[assetId.m_guid] = treeItem;
treeItem->SetEnabled(!m_isFunctionGraphActive);
}
}
}
//////////////////////////////////
// ScriptCanvasNodePaletteConfig
//////////////////////////////////
ScriptCanvasNodePaletteConfig::ScriptCanvasNodePaletteConfig(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel, bool isInContextMenu)
: m_nodePaletteModel(nodePaletteModel)
, m_assetModel(assetModel)
{
m_editorId = ScriptCanvasEditor::AssetEditorId;
m_mimeType = NodePaletteDockWidget::GetMimeType();
m_isInContextMenu = isInContextMenu;
m_allowArrowKeyNavigation = isInContextMenu;
m_saveIdentifier = m_isInContextMenu ? "ScriptCanvas" : "ScriptCanvas_ContextMenu";
m_rootTreeItem = Widget::NodePaletteWidget::ExternalCreateNodePaletteRoot(nodePaletteModel, assetModel);
}
ScriptCanvasNodePaletteConfig::~ScriptCanvasNodePaletteConfig()
{
}
//////////////////////////
// NodePaletteDockWidget
//////////////////////////
NodePaletteDockWidget::NodePaletteDockWidget(const QString& windowLabel, QWidget* parent, const ScriptCanvasNodePaletteConfig& paletteConfig)
: GraphCanvas::NodePaletteDockWidget(parent, windowLabel, paletteConfig)
, m_assetModel(paletteConfig.m_assetModel)
, m_nodePaletteModel(paletteConfig.m_nodePaletteModel)
, m_nextCycleAction(nullptr)
, m_previousCycleAction(nullptr)
, m_ignoreSelectionChanged(false)
{
QMenu* creationMenu = new QMenu();
auto scriptEventAction = creationMenu->addAction("New Script Event");
QObject::connect(scriptEventAction, &QAction::triggered, this, &NodePaletteDockWidget::OnNewCustomEvent);
auto functionAction = creationMenu->addAction("New Function");
QObject::connect(functionAction, &QAction::triggered, this, &NodePaletteDockWidget::OnNewFunctionEvent);
m_newCustomEvent = new QToolButton(this);
m_newCustomEvent->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/add.png"));
m_newCustomEvent->setToolTip("Click to create a new Script Event or Function");
m_newCustomEvent->setPopupMode(QToolButton::ToolButtonPopupMode::InstantPopup);
m_newCustomEvent->setMenu(creationMenu);
//
AddSearchCustomizationWidget(m_newCustomEvent);
GraphCanvas::NodePaletteTreeView* treeView = GetTreeView();
{
m_nextCycleAction = new QAction(treeView);
m_nextCycleAction->setShortcut(QKeySequence(Qt::Key_F8));
treeView->addAction(m_nextCycleAction);
QObject::connect(m_nextCycleAction, &QAction::triggered, this, &NodePaletteDockWidget::CycleToNextNode);
}
{
m_previousCycleAction = new QAction(treeView);
m_previousCycleAction->setShortcut(QKeySequence(Qt::Key_F7));
treeView->addAction(m_previousCycleAction);
QObject::connect(m_previousCycleAction, &QAction::triggered, this, &NodePaletteDockWidget::CycleToPreviousNode);
}
QObject::connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &NodePaletteDockWidget::OnTreeSelectionChanged);
QObject::connect(treeView, &GraphCanvas::NodePaletteTreeView::OnTreeItemDoubleClicked, this, &NodePaletteDockWidget::HandleTreeItemDoubleClicked);
ConfigureSearchCustomizationMargins(QMargins(0, 0, 0, 0), 0);
GraphCanvas::AssetEditorNotificationBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
}
NodePaletteDockWidget::~NodePaletteDockWidget()
{
GraphCanvas::AssetEditorNotificationBus::Handler::BusDisconnect();
GraphCanvas::SceneNotificationBus::Handler::BusDisconnect();
}
void NodePaletteDockWidget::OnNewCustomEvent()
{
AzToolsFramework::AssetEditor::AssetEditorRequestsBus::Broadcast(&AzToolsFramework::AssetEditor::AssetEditorRequests::CreateNewAsset, azrtti_typeid<ScriptEvents::ScriptEventsAsset>());
}
void NodePaletteDockWidget::OnNewFunctionEvent()
{
GeneralRequestBus::Broadcast(&GeneralRequests::CreateNewFunctionAsset);
}
void NodePaletteDockWidget::OnActiveGraphChanged(const GraphCanvas::GraphId& graphCanvasGraphId)
{
GraphCanvas::SceneNotificationBus::Handler::BusDisconnect();
GraphCanvas::SceneNotificationBus::Handler::BusConnect(graphCanvasGraphId);
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId);
static_cast<ScriptCanvasRootPaletteTreeItem*>(ModTreeRoot())->SetActiveScriptCanvasId(scriptCanvasId);
}
void NodePaletteDockWidget::OnSelectionChanged()
{
if (m_ignoreSelectionChanged)
{
return;
}
m_cyclingHelper.Clear();
GetTreeView()->selectionModel()->clearSelection();
}
GraphCanvas::GraphCanvasTreeItem* NodePaletteDockWidget::CreatePaletteRoot() const
{
return NodePaletteWidget::ExternalCreateNodePaletteRoot(m_nodePaletteModel, m_assetModel);
}
void NodePaletteDockWidget::OnTreeSelectionChanged([[maybe_unused]] const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected)
{
ClearCycleTarget();
AZStd::unordered_set< ScriptCanvas::VariableId > variableSet;
GraphCanvas::NodePaletteWidget* paletteWidget = GetNodePaletteWidget();
QModelIndexList indexList = GetTreeView()->selectionModel()->selectedRows();
if (indexList.size() == 1)
{
QSortFilterProxyModel* filterModel = static_cast<QSortFilterProxyModel*>(GetTreeView()->model());
for (const QModelIndex& index : indexList)
{
QModelIndex sourceIndex = filterModel->mapToSource(index);
GraphCanvas::NodePaletteTreeItem* nodePaletteItem = static_cast<GraphCanvas::NodePaletteTreeItem*>(sourceIndex.internalPointer());
ParseCycleTargets(nodePaletteItem);
}
}
}
void NodePaletteDockWidget::AddCycleTarget(ScriptCanvas::NodeTypeIdentifier cyclingIdentifier)
{
if (cyclingIdentifier == ScriptCanvas::NodeTypeIdentifier(0))
{
return;
}
m_cyclingIdentifiers.insert(cyclingIdentifier);
m_cyclingHelper.Clear();
if (m_nextCycleAction)
{
m_nextCycleAction->setEnabled(true);
m_previousCycleAction->setEnabled(true);
}
}
void NodePaletteDockWidget::ClearCycleTarget()
{
m_cyclingIdentifiers.clear();
m_cyclingHelper.Clear();
if (m_nextCycleAction)
{
m_nextCycleAction->setEnabled(false);
m_previousCycleAction->setEnabled(false);
}
}
void NodePaletteDockWidget::CycleToNextNode()
{
ConfigureHelper();
m_cyclingHelper.CycleToNextNode();
}
void NodePaletteDockWidget::CycleToPreviousNode()
{
ConfigureHelper();
m_cyclingHelper.CycleToPreviousNode();
}
void NodePaletteDockWidget::HandleTreeItemDoubleClicked(GraphCanvas::GraphCanvasTreeItem* treeItem)
{
ParseCycleTargets(treeItem);
CycleToNextNode();
}
void NodePaletteDockWidget::ConfigureHelper()
{
if (!m_cyclingHelper.IsConfigured() && !m_cyclingIdentifiers.empty())
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::GetActiveScriptCanvasId);
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::GetActiveGraphCanvasGraphId);
m_cyclingHelper.SetActiveGraph(graphCanvasGraphId);
AZStd::vector<GraphCanvas::NodeId> cyclingNodes;
AZStd::vector<NodeIdPair> completeNodePairs;
for (ScriptCanvas::NodeTypeIdentifier nodeTypeIdentifier : m_cyclingIdentifiers)
{
AZStd::vector<NodeIdPair> nodePairs;
EditorGraphRequestBus::EventResult(nodePairs, scriptCanvasId, &EditorGraphRequests::GetNodesOfType, nodeTypeIdentifier);
cyclingNodes.reserve(cyclingNodes.size() + nodePairs.size());
completeNodePairs.reserve(completeNodePairs.size() + nodePairs.size());
for (const auto& nodeIdPair : nodePairs)
{
cyclingNodes.emplace_back(nodeIdPair.m_graphCanvasId);
completeNodePairs.emplace_back(nodeIdPair);
}
}
m_cyclingHelper.SetNodes(cyclingNodes);
{
// Clean-up Selection to maintain the 'single' selection state throughout the editor
QScopedValueRollback<bool> ignoreSelection(m_ignoreSelectionChanged, true);
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::ClearSelection);
}
EditorGraphRequestBus::Event(scriptCanvasId, &EditorGraphRequests::HighlightNodes, completeNodePairs);
}
}
void NodePaletteDockWidget::ParseCycleTargets(GraphCanvas::GraphCanvasTreeItem* treeItem)
{
AZStd::vector< ScriptCanvas::NodeTypeIdentifier > nodeTypeIdentifiers = NodeIdentifierFactory::ConstructNodeIdentifiers(treeItem);
for (auto nodeTypeIdentifier : nodeTypeIdentifiers)
{
AddCycleTarget(nodeTypeIdentifier);
}
}
}
}
#include <Editor/View/Widgets/moc_ScriptCanvasNodePaletteDockWidget.cpp>
@@ -0,0 +1,205 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <AzCore/Component/EntityId.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeCategorizer.h>
#include <GraphCanvas/Widgets/NodePalette/NodePaletteWidget.h>
#include <GraphCanvas/Widgets/NodePalette/NodePaletteDockWidget.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/NodePaletteTreeItem.h>
#include <GraphCanvas/Editor/AssetEditorBus.h>
#include <GraphCanvas/Utils/GraphUtils.h>
#include <ScriptCanvas/Components/EditorUtils.h>
#include <ScriptCanvas/Core/Core.h>
#endif
class QToolButton;
namespace ScriptCanvasEditor { class FunctionPaletteTreeItem; }
namespace Ui
{
class ScriptCanvasNodePaletteToolbar;
}
namespace ScriptCanvasEditor
{
class ScriptEventsPaletteTreeItem;
class NodePaletteModel;
namespace Widget
{
struct NodePaletteWidget
{
static GraphCanvas::NodePaletteTreeItem* ExternalCreateNodePaletteRoot(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel);
};
class ScriptCanvasRootPaletteTreeItem
: public GraphCanvas::NodePaletteTreeItem
, AzFramework::AssetCatalogEventBus::Handler
, AZ::Data::AssetBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(ScriptCanvasRootPaletteTreeItem, AZ::SystemAllocator, 0);
ScriptCanvasRootPaletteTreeItem(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel);
~ScriptCanvasRootPaletteTreeItem();
void RegisterCategoryNode(GraphCanvas::GraphCanvasTreeItem* treeItem, const char* subCategory, GraphCanvas::NodePaletteTreeItem* parentRoot = nullptr);
GraphCanvas::NodePaletteTreeItem* GetCategoryNode(const char* categoryPath, GraphCanvas::NodePaletteTreeItem* parentRoot = nullptr);
void PruneEmptyNodes();
void SetActiveScriptCanvasId(const ScriptCanvas::ScriptCanvasId& assetId);
private:
void OnRowsInserted(const QModelIndex& parentIndex, int first, int last);
void OnRowsAboutToBeRemoved(const QModelIndex& parentIndex, int first, int last);
void TraverseTree(QModelIndex index = QModelIndex());
void ProcessAsset(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
// AssetCatalogEventBus
void OnCatalogAssetChanged(const AZ::Data::AssetId& /*assetId*/) override;
void OnCatalogAssetAdded(const AZ::Data::AssetId& /*assetId*/) override;
void OnCatalogAssetRemoved(const AZ::Data::AssetId& /*assetId*/, const AZ::Data::AssetInfo& /*assetInfo*/) override;
////
const NodePaletteModel& m_nodePaletteModel;
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_assetModel;
GraphCanvas::GraphCanvasTreeCategorizer m_categorizer;
bool m_isFunctionGraphActive;
AZ::Data::AssetId m_previousAssetId;
AZStd::unordered_map< AZ::Data::AssetId, ScriptEventsPaletteTreeItem* > m_scriptEventElementTreeItems;
AZStd::unordered_map< AZ::Data::AssetId, FunctionPaletteTreeItem* > m_globalFunctionTreeItems;
// RequestAssetLoad uses this set to track assets being asynchronously loaded
AZStd::unordered_set<AZStd::pair<AZ::Data::AssetId, AZ::Data::AssetType>> m_pendingAssets;
// Requests an async load of a given asset of a type
void RequestAssetLoad(AZ::Data::AssetId assetId, AZ::Data::AssetType assetType);
// When the asset loading is ready, this allows to use the asset in some meaningful way
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
AZStd::vector< QMetaObject::Connection > m_lambdaConnections;
};
class ScriptCanvasNodePaletteToolbar
: public QWidget
{
Q_OBJECT
public:
enum class FilterType
{
AllNodes
};
ScriptCanvasNodePaletteToolbar(QWidget* parent);
signals:
void OnFilterChanged(FilterType newFilter);
void CreateDynamicEBus();
private:
AZStd::unique_ptr< Ui::ScriptCanvasNodePaletteToolbar > m_ui;
};
class ScriptCanvasNodePaletteConfig
: public GraphCanvas::NodePaletteConfig
{
public:
ScriptCanvasNodePaletteConfig(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* assetModel, bool isInContextMenu);
~ScriptCanvasNodePaletteConfig();
const NodePaletteModel& m_nodePaletteModel;
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_assetModel;
};
class NodePaletteDockWidget
: public GraphCanvas::NodePaletteDockWidget
, public GraphCanvas::AssetEditorNotificationBus::Handler
, public GraphCanvas::SceneNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(NodePaletteDockWidget, AZ::SystemAllocator, 0);
static const char* GetMimeType() { return "scriptcanvas/node-palette-mime-event"; }
NodePaletteDockWidget(const QString& windowLabel, QWidget* parent, const ScriptCanvasNodePaletteConfig& paletteConfig);
~NodePaletteDockWidget();
void OnNewCustomEvent();
void OnNewFunctionEvent();
// GraphCanvas::AssetEditorNotificationBus::Handler
void OnActiveGraphChanged(const GraphCanvas::GraphId& graphCanvasGraphId) override;
////
// GraphCanvas::SceneNotificationBus
void OnSelectionChanged() override;
////
protected:
GraphCanvas::GraphCanvasTreeItem* CreatePaletteRoot() const override;
void OnTreeSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void AddCycleTarget(ScriptCanvas::NodeTypeIdentifier cyclingIdentifier);
void ClearCycleTarget();
void CycleToNextNode();
void CycleToPreviousNode();
private:
void HandleTreeItemDoubleClicked(GraphCanvas::GraphCanvasTreeItem* treeItem);
void ConfigureHelper();
void ParseCycleTargets(GraphCanvas::GraphCanvasTreeItem* treeItem);
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_assetModel;
const NodePaletteModel& m_nodePaletteModel;
QToolButton* m_newCustomEvent;
AZStd::unordered_set< ScriptCanvas::NodeTypeIdentifier > m_cyclingIdentifiers;
GraphCanvas::NodeFocusCyclingHelper m_cyclingHelper;
QAction* m_nextCycleAction;
QAction* m_previousCycleAction;
bool m_ignoreSelectionChanged;
};
}
}
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScriptCanvasNodePaletteToolbar</class>
<widget class="QWidget" name="ScriptCanvasNodePaletteToolbar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>32</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QComboBox" name="nodeTypeFilter">
<property name="enabled">
<bool>false</bool>
</property>
<item>
<property name="text">
<string>All Nodes</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QToolButton" name="newButton">
<property name="toolTip">
<string>Create Custom Event</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/add.png</normaloff>:/ScriptCanvasEditorResources/Resources/add.png</iconset>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../Windows/ScriptCanvasEditorResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,345 @@
/*
* 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 <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <Editor/Assets/ScriptCanvasAssetTrackerBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/View/Widgets/StatisticsDialog/NodeUsageTreeItem.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <ScriptCanvas/Components/EditorGraph.h>
namespace ScriptCanvasEditor
{
/////////////////////////////////
// NodePaletteNodeUsageRootItem
/////////////////////////////////
NodePaletteNodeUsageRootItem::NodePaletteNodeUsageRootItem(const NodePaletteModel& nodePaletteModel)
: GraphCanvas::NodePaletteTreeItem("root", ScriptCanvasEditor::AssetEditorId)
, m_nodePaletteModel(nodePaletteModel)
, m_categorizer(nodePaletteModel)
{
}
NodePaletteNodeUsageRootItem::~NodePaletteNodeUsageRootItem()
{
}
GraphCanvas::NodePaletteTreeItem* NodePaletteNodeUsageRootItem::GetCategoryNode(const char* categoryPath, GraphCanvas::NodePaletteTreeItem* parentRoot)
{
if (parentRoot)
{
return static_cast<GraphCanvas::NodePaletteTreeItem*>(m_categorizer.GetCategoryNode(categoryPath, parentRoot));
}
else
{
return static_cast<GraphCanvas::NodePaletteTreeItem*>(m_categorizer.GetCategoryNode(categoryPath, this));
}
}
void NodePaletteNodeUsageRootItem::PruneEmptyNodes()
{
m_categorizer.PruneEmptyNodes();
}
////////////////////////////////////
// NodePaletteNodeUsagePaletteItem
////////////////////////////////////
NodePaletteNodeUsagePaletteItem::NodePaletteNodeUsagePaletteItem(const ScriptCanvas::NodeTypeIdentifier& nodeIdentifier, AZStd::string_view displayName)
: GraphCanvas::IconDecoratedNodePaletteTreeItem(displayName, ScriptCanvasEditor::AssetEditorId)
, m_nodeIdentifier(nodeIdentifier)
{
}
NodePaletteNodeUsagePaletteItem::~NodePaletteNodeUsagePaletteItem()
{
}
const ScriptCanvas::NodeTypeIdentifier& NodePaletteNodeUsagePaletteItem::GetNodeTypeIdentifier() const
{
return m_nodeIdentifier;
}
//////////////////////////////
// ScriptCanvasAssetTreeItem
//////////////////////////////
ScriptCanvasAssetNodeUsageTreeItem::ScriptCanvasAssetNodeUsageTreeItem(AZStd::string_view assetName)
: m_name(QString::fromUtf8(assetName.data(), static_cast<int>(assetName.size())))
, m_icon(":/ScriptCanvasEditorResources/Resources/edit_icon.png")
, m_activeIdentifier(0)
{
}
int ScriptCanvasAssetNodeUsageTreeItem::GetColumnCount() const
{
return Column::Count;
}
QVariant ScriptCanvasAssetNodeUsageTreeItem::Data(const QModelIndex& index, int role) const
{
if (index.column() == Column::Name)
{
switch (role)
{
case Qt::DisplayRole:
return GetName();
case Qt::DecorationRole:
break;
default:
break;
}
}
else if (index.column() == Column::UsageCount && m_assetId.IsValid())
{
switch (role)
{
case Qt::DisplayRole:
return GetNodeCount();
default:
break;
}
}
else if (index.column() == Column::OpenIcon)
{
if (m_assetId.IsValid())
{
switch (role)
{
case Qt::DecorationRole:
return m_icon;
default:
break;
}
}
}
return QVariant();
}
Qt::ItemFlags ScriptCanvasAssetNodeUsageTreeItem::Flags([[maybe_unused]] const QModelIndex& index) const
{
Qt::ItemFlags baseFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return baseFlags;
}
void ScriptCanvasAssetNodeUsageTreeItem::SetAssetId(const AZ::Data::AssetId& assetId)
{
// If we are setting up a new assetId, we wantt o register for the bus.
// Otherwise we just want to reload the asset to scrape some data from it.
if (m_assetId != assetId)
{
if (AZ::Data::AssetBus::Handler::BusIsConnected())
{
AZ::Data::AssetBus::Handler::BusDisconnect();
}
m_assetId = assetId;
AZ::Data::AssetBus::Handler::BusConnect(assetId);
}
AZ::Data::Asset<ScriptCanvasAsset> newAsset;
const bool loadBlocking = false;
auto onAssetReady = [](ScriptCanvasMemoryAsset&) {};
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::Load, m_assetId, azrtti_typeid<ScriptCanvasAsset>(), onAssetReady);
ProcessAsset(newAsset);
}
const AZ::Data::AssetId& ScriptCanvasAssetNodeUsageTreeItem::GetAssetId() const
{
return m_assetId;
}
const QString& ScriptCanvasAssetNodeUsageTreeItem::GetName() const
{
return m_name;
}
void ScriptCanvasAssetNodeUsageTreeItem::SetActiveNodeType(const ScriptCanvas::NodeTypeIdentifier& nodeTypeIdentifier)
{
if (m_activeIdentifier != nodeTypeIdentifier)
{
m_activeIdentifier = nodeTypeIdentifier;
SignalDataChanged();
}
}
int ScriptCanvasAssetNodeUsageTreeItem::GetNodeCount() const
{
auto nodeIter = m_statisticsHelper.m_nodeIdentifierCount.find(m_activeIdentifier);
if (nodeIter == m_statisticsHelper.m_nodeIdentifierCount.end())
{
return 0;
}
return nodeIter->second;
}
void ScriptCanvasAssetNodeUsageTreeItem::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
ProcessAsset(asset);
}
void ScriptCanvasAssetNodeUsageTreeItem::OnAssetSaved(AZ::Data::Asset<AZ::Data::AssetData> asset, bool isSuccessful)
{
if (isSuccessful)
{
ProcessAsset(asset);
}
}
void ScriptCanvasAssetNodeUsageTreeItem::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
ProcessAsset(asset);
}
void ScriptCanvasAssetNodeUsageTreeItem::ProcessAsset(const AZ::Data::Asset<ScriptCanvasAsset>& scriptCanvasAsset)
{
if (scriptCanvasAsset.IsReady())
{
AZ::Entity* scriptCanvasEntity = scriptCanvasAsset.Get()->GetScriptCanvasEntity();
Graph* editorGraph = AZ::EntityUtils::FindFirstDerivedComponent<Graph>(scriptCanvasEntity);
if (editorGraph)
{
m_statisticsHelper = editorGraph->GetNodeUsageStatistics();
// Temporary measure to deal potentially unfilled out data.
if (m_statisticsHelper.m_nodeIdentifierCount.empty())
{
m_statisticsHelper.PopulateStatisticData(editorGraph);
}
SignalDataChanged();
}
}
}
///////////////////////////////////////////
// ScriptCanvasAssetNodeUsageTreeItemRoot
///////////////////////////////////////////
ScriptCanvasAssetNodeUsageTreeItemRoot::ScriptCanvasAssetNodeUsageTreeItemRoot()
: ScriptCanvasAssetNodeUsageTreeItem("root")
, m_categorizer((*this))
{
}
void ScriptCanvasAssetNodeUsageTreeItemRoot::RegisterAsset(const AZ::Data::AssetId& assetId)
{
auto asset = AZ::Data::AssetManager::Instance().GetAsset(assetId, azrtti_typeid<ScriptCanvasAsset>(), AZ::Data::AssetLoadBehavior::Default);
if (!asset.IsReady())
{
// The asset must be loaded before it an be registered. We will connect to the AssetBus and wait for it to be ready.
AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
return;
}
auto treeItem = GetAssetItem(assetId);
if (treeItem == nullptr)
{
AZ::Data::AssetInfo assetInfo;
const AZStd::string platformName = ""; // Empty for default
AZStd::string rootFilePath;
bool foundPath = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundPath, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetInfoById, assetId, azrtti_typeid<ScriptCanvasAsset>(), platformName, assetInfo, rootFilePath);
if (foundPath)
{
AZStd::string relativePath = assetInfo.m_relativePath;
AZStd::string fileName;
if (AzFramework::StringFunc::Path::GetFileName(relativePath.c_str(), fileName))
{
AzFramework::StringFunc::Path::Normalize(relativePath);
const bool stripLastComponent = true;
if (AzFramework::StringFunc::Path::StripComponent(relativePath, stripLastComponent))
{
AzFramework::StringFunc::Replace(relativePath, AZ_CORRECT_FILESYSTEM_SEPARATOR, '/');
GraphCanvas::GraphCanvasTreeItem* treeItem2 = m_categorizer.GetCategoryNode(relativePath.c_str(), this);
if (treeItem2)
{
ScriptCanvasAssetNodeUsageTreeItem* usageTreeItem = treeItem2->CreateChildNode<ScriptCanvasAssetNodeUsageTreeItem>(fileName);
usageTreeItem->SetAssetId(assetId);
m_scriptCanvasAssetItems[assetId] = usageTreeItem;
}
}
}
}
}
else
{
treeItem->SetAssetId(assetId);
}
}
void ScriptCanvasAssetNodeUsageTreeItemRoot::RemoveAsset(const AZ::Data::AssetId& assetId)
{
auto assetIter = m_scriptCanvasAssetItems.find(assetId);
if (assetIter != m_scriptCanvasAssetItems.end())
{
assetIter->second->DetachItem();
delete assetIter->second;
m_categorizer.PruneEmptyNodes();
}
}
ScriptCanvasAssetNodeUsageTreeItem* ScriptCanvasAssetNodeUsageTreeItemRoot::GetAssetItem(const AZ::Data::AssetId& assetId)
{
auto assetIter = m_scriptCanvasAssetItems.find(assetId);
if (assetIter != m_scriptCanvasAssetItems.end())
{
return assetIter->second;
}
return nullptr;
}
GraphCanvas::GraphCanvasTreeItem* ScriptCanvasAssetNodeUsageTreeItemRoot::CreateCategoryNode([[maybe_unused]] AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvasTreeItem* parent) const
{
return parent->CreateChildNode<ScriptCanvasAssetNodeUsageTreeItem>(categoryName);
}
const ScriptCanvasAssetNodeUsageTreeItemRoot::ScriptCanvasAssetMap& ScriptCanvasAssetNodeUsageTreeItemRoot::GetAssetTreeItems() const
{
return m_scriptCanvasAssetItems;
}
void ScriptCanvasAssetNodeUsageTreeItemRoot::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
AZ::Data::AssetBus::MultiHandler::BusDisconnect(*AZ::Data::AssetBus::GetCurrentBusId());
RegisterAsset(asset.GetId());
}
}
@@ -0,0 +1,155 @@
/*
* 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 <QIcon>
#include <GraphCanvas/Editor/EditorTypes.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeCategorizer.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/IconDecoratedNodePaletteTreeItem.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <ScriptCanvas/Components/EditorUtils.h>
namespace ScriptCanvasEditor
{
// NodePaletteItems
class NodePaletteNodeUsageRootItem
: public GraphCanvas::NodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(NodePaletteNodeUsageRootItem, AZ::SystemAllocator, 0);
AZ_RTTI(NodePaletteNodeUsageRootItem, "{ED21874C-6955-40F0-B451-F5FF5A16CF71}", GraphCanvas::NodePaletteTreeItem);
NodePaletteNodeUsageRootItem(const NodePaletteModel& nodePaletteModel);
~NodePaletteNodeUsageRootItem();
GraphCanvas::NodePaletteTreeItem* GetCategoryNode(const char* categoryPath, GraphCanvas::NodePaletteTreeItem* parentRoot = nullptr);
void PruneEmptyNodes();
private:
const NodePaletteModel& m_nodePaletteModel;
GraphCanvas::GraphCanvasTreeCategorizer m_categorizer;
};
class NodePaletteNodeUsagePaletteItem
: public GraphCanvas::IconDecoratedNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(NodePaletteNodeUsagePaletteItem, AZ::SystemAllocator, 0);
AZ_RTTI(NodePaletteNodeUsagePaletteItem, "{CA8E31A8-56CA-49A2-80F2-68A1E3A9EDF6}", GraphCanvas::IconDecoratedNodePaletteTreeItem);
NodePaletteNodeUsagePaletteItem(const ScriptCanvas::NodeTypeIdentifier& nodeIdentifier, AZStd::string_view displayName);
~NodePaletteNodeUsagePaletteItem();
const ScriptCanvas::NodeTypeIdentifier& GetNodeTypeIdentifier() const;
private:
ScriptCanvas::NodeTypeIdentifier m_nodeIdentifier;
};
////
// General TreeItems
class ScriptCanvasAssetNodeUsageTreeItem
: public GraphCanvas::GraphCanvasTreeItem
, public AZ::Data::AssetBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ScriptCanvasAssetNodeUsageTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(ScriptCanvasAssetNodeUsageTreeItem, "{1FF437D9-5159-49CD-8D80-8AC3334886E8}", GraphCanvas::GraphCanvasTreeItem);
enum Column
{
IndexForce = -1,
Name,
UsageCount,
OpenIcon,
Count
};
ScriptCanvasAssetNodeUsageTreeItem(AZStd::string_view assetName);
~ScriptCanvasAssetNodeUsageTreeItem() = default;
int GetColumnCount() const override final;
QVariant Data(const QModelIndex& index, int role) const override final;
Qt::ItemFlags Flags(const QModelIndex& index) const override final;
void SetAssetId(const AZ::Data::AssetId& assetId);
const AZ::Data::AssetId& GetAssetId() const;
const QString& GetName() const;
void SetActiveNodeType(const ScriptCanvas::NodeTypeIdentifier& nodeTypeIdentifier);
int GetNodeCount() const;
// AZ::Data::AssetBus::Handler
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetSaved(AZ::Data::Asset<AZ::Data::AssetData> asset, bool isSuccessful) override;
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
////
private:
void ProcessAsset(const AZ::Data::Asset<ScriptCanvasAsset>& scriptCanvasAsset);
QString m_name;
QIcon m_icon;
ScriptCanvas::NodeTypeIdentifier m_activeIdentifier;
AZ::Data::AssetId m_assetId;
GraphStatisticsHelper m_statisticsHelper;
};
class ScriptCanvasAssetNodeUsageTreeItemRoot
: public ScriptCanvasAssetNodeUsageTreeItem
, public GraphCanvas::CategorizerInterface
, public AZ::Data::AssetBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(ScriptCanvasAssetNodeUsageTreeItemRoot, AZ::SystemAllocator, 0);
AZ_RTTI(ScriptCanvasAssetNodeUsageTreeItemRoot, "{EDCBFE97-0BF9-4AE5-8C6E-C4805E08CBFC}", ScriptCanvasAssetNodeUsageTreeItem);
typedef AZStd::unordered_map< AZ::Data::AssetId, ScriptCanvasAssetNodeUsageTreeItem* > ScriptCanvasAssetMap;
ScriptCanvasAssetNodeUsageTreeItemRoot();
~ScriptCanvasAssetNodeUsageTreeItemRoot() = default;
void RegisterAsset(const AZ::Data::AssetId& assetId);
void RemoveAsset(const AZ::Data::AssetId& assetId);
ScriptCanvasAssetNodeUsageTreeItem* GetAssetItem(const AZ::Data::AssetId& assetId);
// CategorizerInterface
GraphCanvas::GraphCanvasTreeItem* CreateCategoryNode(AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvasTreeItem* parent) const override;
////
const ScriptCanvasAssetMap& GetAssetTreeItems() const;
private:
// AZ::Data::AssetBus::MultiHandler
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
GraphCanvas::GraphCanvasTreeCategorizer m_categorizer;
ScriptCanvasAssetMap m_scriptCanvasAssetItems;
};
////
}
@@ -0,0 +1,463 @@
/*
* 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 <AzCore/Asset/AssetManagerBus.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <Editor/View/Widgets/StatisticsDialog/ScriptCanvasStatisticsDialog.h>
#include <Editor/View/Widgets/StatisticsDialog/ui_ScriptCanvasStatisticsDialog.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <ScriptCanvas/Bus/RequestBus.h>
namespace
{
ScriptCanvasEditor::NodePaletteNodeUsageRootItem* ExternalCreatePaletteRoot(const ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, AZStd::unordered_map< ScriptCanvas::NodeTypeIdentifier, GraphCanvas::GraphCanvasTreeItem* >& leafMap)
{
ScriptCanvasEditor::NodePaletteNodeUsageRootItem* root = aznew ScriptCanvasEditor::NodePaletteNodeUsageRootItem(nodePaletteModel);
const ScriptCanvasEditor::NodePaletteModel::NodePaletteRegistry& nodeRegistry = nodePaletteModel.GetNodeRegistry();
for (const auto& registryPair : nodeRegistry)
{
const ScriptCanvasEditor::NodePaletteModelInformation* modelInformation = registryPair.second;
GraphCanvas::GraphCanvasTreeItem* parentItem = root->GetCategoryNode(modelInformation->m_categoryPath.c_str());
GraphCanvas::NodePaletteTreeItem* createdItem = nullptr;
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::NodePaletteNodeUsagePaletteItem>(modelInformation->m_nodeIdentifier, modelInformation->m_displayName);
if (createdItem)
{
modelInformation->PopulateTreeItem((*createdItem));
leafMap[modelInformation->m_nodeIdentifier] = createdItem;
}
}
root->PruneEmptyNodes();
return root;
}
}
namespace ScriptCanvasEditor
{
//////////////////////////////////////////
// ScriptCanvasAssetNodeUsageFilterModel
//////////////////////////////////////////
ScriptCanvasAssetNodeUsageFilterModel::ScriptCanvasAssetNodeUsageFilterModel()
: m_nodeIdentifier(0)
{
}
bool ScriptCanvasAssetNodeUsageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
// Never want to show something if we don't have a node type identifier
if (m_nodeIdentifier == 0)
{
return false;
}
QAbstractItemModel* model = sourceModel();
QModelIndex index = model->index(sourceRow, 0, sourceParent);
ScriptCanvasAssetNodeUsageTreeItem* treeItem = static_cast<ScriptCanvasAssetNodeUsageTreeItem*>(index.internalPointer());
if (!treeItem->GetAssetId().IsValid())
{
for (int i = 0; i < treeItem->GetChildCount(); ++i)
{
if (filterAcceptsRow(i, index))
{
return true;
}
}
}
else
{
treeItem->SetActiveNodeType(m_nodeIdentifier);
if (treeItem->GetNodeCount() == 0)
{
return false;
}
bool showRow = m_filter.isEmpty();
if (!showRow)
{
const QString& name = treeItem->GetName();
int regexIndex = name.lastIndexOf(m_regex);
showRow = regexIndex >= 0;
if (!showRow)
{
ScriptCanvasAssetNodeUsageTreeItem* parentItem = static_cast<ScriptCanvasAssetNodeUsageTreeItem*>(treeItem->GetParent());
while (!showRow && parentItem)
{
ScriptCanvasAssetNodeUsageTreeItem* nextItem = static_cast<ScriptCanvasAssetNodeUsageTreeItem*>(parentItem->GetParent());
// This means we are the root element. And we don't want to match based on it.
if (nextItem == nullptr)
{
break;
}
const QString& parentName = parentItem->GetName();
int regexIndex2 = parentName.lastIndexOf(m_regex);
if (regexIndex2 >= 0)
{
showRow = true;
}
parentItem = nextItem;
}
}
}
return showRow;
}
return false;
}
void ScriptCanvasAssetNodeUsageFilterModel::SetFilter(const QString& filterName)
{
m_filter = filterName;
m_regex = QRegExp(m_filter, Qt::CaseInsensitive);
invalidate();
}
void ScriptCanvasAssetNodeUsageFilterModel::SetNodeTypeFilter(const ScriptCanvas::NodeTypeIdentifier& nodeType)
{
m_nodeIdentifier = nodeType;
invalidate();
}
/////////////////////
// StatisticsDialog
/////////////////////
StatisticsDialog::StatisticsDialog(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* scriptCanvasAssetBrowserModel, QWidget* widget)
: QDialog(widget)
, m_nodePaletteModel(nodePaletteModel)
, m_ui(new Ui::ScriptCanvasStatisticsDialog())
, m_treeRoot(nullptr)
, m_scriptCanvasAssetBrowserModel(scriptCanvasAssetBrowserModel)
, m_scriptCanvasAssetTreeRoot(nullptr)
, m_scriptCanvasAssetTree(nullptr)
, m_scriptCanvasAssetFilterModel(nullptr)
{
setWindowFlags(Qt::WindowFlags::enum_type::WindowCloseButtonHint);
m_ui->setupUi(this);
AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:Editor.qss"));
}
StatisticsDialog::~StatisticsDialog()
{
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void StatisticsDialog::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
{
if (m_scriptCanvasAssetTreeRoot)
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, assetId);
if (assetInfo.m_assetId.IsValid())
{
if (assetInfo.m_assetType == azrtti_typeid<ScriptCanvasAsset>()
|| assetInfo.m_assetType == azrtti_typeid<ScriptCanvas::ScriptCanvasFunctionAsset>())
{
m_scriptCanvasAssetTreeRoot->RegisterAsset(assetId);
}
}
}
}
void StatisticsDialog::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
{
OnCatalogAssetChanged(assetId);
}
void StatisticsDialog::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& /*assetInfo*/)
{
// at this point, the asset is gone. You can't search for it in the catalog.
if (m_scriptCanvasAssetTreeRoot)
{
m_scriptCanvasAssetTreeRoot->RemoveAsset(assetId);
}
}
void StatisticsDialog::OnAssetModelRepopulated()
{
ResetModel();
}
void StatisticsDialog::OnAssetNodeAdded(NodePaletteModelInformation* modelInformation)
{
auto leafIter = m_leafMap.find(modelInformation->m_nodeIdentifier);
if (leafIter != m_leafMap.end())
{
// Duplicate Id. Ignore for now.
return;
}
GraphCanvas::GraphCanvasTreeItem* parentItem = m_treeRoot->GetCategoryNode(modelInformation->m_categoryPath.c_str());
GraphCanvas::NodePaletteTreeItem* createdItem = nullptr;
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::NodePaletteNodeUsagePaletteItem>(modelInformation->m_nodeIdentifier, modelInformation->m_displayName);
if (createdItem)
{
modelInformation->PopulateTreeItem((*createdItem));
m_leafMap[modelInformation->m_nodeIdentifier] = createdItem;
}
else
{
m_treeRoot->PruneEmptyNodes();
}
}
void StatisticsDialog::OnAssetNodeRemoved(NodePaletteModelInformation* modelInformation)
{
auto leafIter = m_leafMap.find(modelInformation->m_nodeIdentifier);
if (leafIter != m_leafMap.end())
{
leafIter->second->DetachItem();
delete leafIter->second;
m_leafMap.erase(leafIter);
m_treeRoot->PruneEmptyNodes();
}
}
void StatisticsDialog::OnScriptCanvasAssetClicked(const QModelIndex& index)
{
if (index.isValid())
{
if (index.column() == ScriptCanvasAssetNodeUsageTreeItem::Column::OpenIcon)
{
QModelIndex sourceIndex = m_scriptCanvasAssetFilterModel->mapToSource(index);
ScriptCanvasAssetNodeUsageTreeItem* treeItem = static_cast<ScriptCanvasAssetNodeUsageTreeItem*>(sourceIndex.internalPointer());
if (treeItem->GetAssetId().IsValid())
{
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAssetId, treeItem->GetAssetId());
}
}
}
}
void StatisticsDialog::showEvent(QShowEvent* showEvent)
{
InitStatisticsWindow();
QDialog::showEvent(showEvent);
}
void StatisticsDialog::OnSelectionCleared()
{
m_scriptCanvasAssetFilterModel->SetNodeTypeFilter(0);
m_ui->statDisplayName->setText("N/A");
m_ui->totalUsageCount->setText(QString::number(0));
m_ui->uniqueGraphsCount->setText(QString::number(0));
m_ui->averageGraphUsages->setText(QString::number(0));
}
void StatisticsDialog::OnItemSelected(const GraphCanvas::GraphCanvasTreeItem* treeItem)
{
const NodePaletteNodeUsagePaletteItem* usageItem = azrtti_cast<const NodePaletteNodeUsagePaletteItem*>(treeItem);
if (usageItem)
{
m_scriptCanvasAssetFilterModel->SetNodeTypeFilter(usageItem->GetNodeTypeIdentifier());
m_ui->scriptCanvasAssetTree->expandAll();
const ScriptCanvasAssetNodeUsageTreeItemRoot::ScriptCanvasAssetMap& assetMapping = m_scriptCanvasAssetTreeRoot->GetAssetTreeItems();
int totalNodeCount = 0;
int uniqueGraphs = 0;
for (auto itemPair : assetMapping)
{
int nodeCount = itemPair.second->GetNodeCount();
if (nodeCount > 0)
{
totalNodeCount += nodeCount;
uniqueGraphs++;
}
}
m_ui->statDisplayName->setText(usageItem->GetName());
m_ui->totalUsageCount->setText(QString::number(totalNodeCount));
m_ui->uniqueGraphsCount->setText(QString::number(uniqueGraphs));
float averageUses = 0;
if (uniqueGraphs != 0)
{
averageUses = static_cast<float>(totalNodeCount) / static_cast<float>(uniqueGraphs);
}
m_ui->averageGraphUsages->setText(QString::number(averageUses, 'g', 2));
}
else
{
OnSelectionCleared();
}
}
void StatisticsDialog::OnFilterUpdated(const QString& filterText)
{
m_scriptCanvasAssetFilterModel->SetFilter(filterText);
m_ui->scriptCanvasAssetTree->expandAll();
}
void StatisticsDialog::OnScriptCanvasAssetRowsInserted(QModelIndex parentIndex, int first, int last)
{
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = m_scriptCanvasAssetBrowserModel->index(first, 0, parentIndex);
QModelIndex sourceIndex = m_scriptCanvasAssetBrowserModel->mapToSource(modelIndex);
const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessAsset(entry);
}
}
void StatisticsDialog::InitStatisticsWindow()
{
if (m_treeRoot == nullptr)
{
m_treeRoot = ExternalCreatePaletteRoot(m_nodePaletteModel, m_leafMap);
GraphCanvas::NodePaletteConfig paletteConfig;
paletteConfig.m_rootTreeItem = m_treeRoot;
paletteConfig.m_editorId = ScriptCanvasEditor::AssetEditorId;
paletteConfig.m_mimeType = "";
paletteConfig.m_isInContextMenu = false;
paletteConfig.m_saveIdentifier = "ScriptCanvas_UsageStatistics";
paletteConfig.m_clearSelectionOnSceneChange = false;
paletteConfig.m_allowArrowKeyNavigation = true;
m_ui->nodePaletteWidget->SetupNodePalette(paletteConfig);
m_scriptCanvasAssetTreeRoot = aznew ScriptCanvasAssetNodeUsageTreeItemRoot();
m_scriptCanvasAssetTree = aznew GraphCanvas::GraphCanvasTreeModel(m_scriptCanvasAssetTreeRoot);
m_scriptCanvasAssetFilterModel = aznew ScriptCanvasAssetNodeUsageFilterModel();
m_scriptCanvasAssetFilterModel->setSourceModel(m_scriptCanvasAssetTree);
TraverseTree();
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
m_ui->scriptCanvasAssetTree->setModel(m_scriptCanvasAssetFilterModel);
m_ui->splitter->setStretchFactor(0, 1);
m_ui->splitter->setStretchFactor(1, 2);
m_ui->searchWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
m_ui->scriptCanvasAssetTree->header()->setSectionResizeMode(ScriptCanvasAssetNodeUsageTreeItem::Column::Name, QHeaderView::ResizeMode::ResizeToContents);
m_ui->scriptCanvasAssetTree->header()->setSectionResizeMode(ScriptCanvasAssetNodeUsageTreeItem::Column::UsageCount, QHeaderView::ResizeMode::Fixed);
m_ui->scriptCanvasAssetTree->header()->resizeSection(ScriptCanvasAssetNodeUsageTreeItem::Column::UsageCount, 30);
QObject::connect(m_ui->scriptCanvasAssetTree, &QTreeView::clicked, this, &StatisticsDialog::OnScriptCanvasAssetClicked);
QObject::connect(m_ui->nodePaletteWidget, &GraphCanvas::NodePaletteWidget::OnSelectionCleared, this, &StatisticsDialog::OnSelectionCleared);
QObject::connect(m_ui->nodePaletteWidget, &GraphCanvas::NodePaletteWidget::OnTreeItemSelected, this, &StatisticsDialog::OnItemSelected);
QObject::connect(m_ui->searchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, this, &StatisticsDialog::OnFilterUpdated);
QObject::connect(m_scriptCanvasAssetBrowserModel, &QAbstractItemModel::rowsInserted, this, &StatisticsDialog::OnScriptCanvasAssetRowsInserted);
OnSelectionCleared();
NodePaletteModelNotificationBus::Handler::BusConnect(m_nodePaletteModel.GetNotificationId());
}
}
void StatisticsDialog::ResetModel()
{
if (m_treeRoot)
{
m_leafMap.clear();
m_treeRoot = ExternalCreatePaletteRoot(m_nodePaletteModel, m_leafMap);
m_ui->nodePaletteWidget->ResetModel(m_treeRoot);
}
}
void StatisticsDialog::TraverseTree(QModelIndex index)
{
QModelIndex sourceIndex = m_scriptCanvasAssetBrowserModel->mapToSource(index);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessAsset(entry);
int rowCount = m_scriptCanvasAssetBrowserModel->rowCount(index);
for (int i = 0; i < rowCount; ++i)
{
QModelIndex nextIndex = m_scriptCanvasAssetBrowserModel->index(i, 0, index);
TraverseTree(nextIndex);
}
}
void StatisticsDialog::ProcessAsset(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
if (entry)
{
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = static_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
if (productEntry->GetAssetType() == azrtti_typeid<ScriptCanvasAsset>())
{
const AZ::Data::AssetId& assetId = productEntry->GetAssetId();
m_scriptCanvasAssetTreeRoot->RegisterAsset(assetId);
}
}
}
}
#include <Editor/View/Widgets/StatisticsDialog/moc_ScriptCanvasStatisticsDialog.cpp>
}
@@ -0,0 +1,123 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QAbstractItemModel>
#include <QDialog>
#include <QSortFilterProxyModel>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeModel.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <Editor/View/Widgets/StatisticsDialog/NodeUsageTreeItem.h>
#include <ScriptCanvas/Core/Core.h>
#endif
namespace Ui
{
class ScriptCanvasStatisticsDialog;
}
namespace ScriptCanvasEditor
{
class ScriptCanvasAssetNodeUsageFilterModel
: public QSortFilterProxyModel
{
public:
AZ_CLASS_ALLOCATOR(ScriptCanvasAssetNodeUsageFilterModel, AZ::SystemAllocator, 0);
ScriptCanvasAssetNodeUsageFilterModel();
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const;
void SetFilter(const QString& filterName);
void SetNodeTypeFilter(const ScriptCanvas::NodeTypeIdentifier& nodeType);
private:
QString m_filter;
QRegExp m_regex;
ScriptCanvas::NodeTypeIdentifier m_nodeIdentifier;
};
class StatisticsDialog
: public QDialog
, AzFramework::AssetCatalogEventBus::Handler
, public NodePaletteModelNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(StatisticsDialog, AZ::SystemAllocator, 0);
StatisticsDialog(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* scriptCanvasAssetModel, QWidget* widget = nullptr);
~StatisticsDialog();
void InitStatisticsWindow();
void ResetModel();
// AssetSystemBus
//! Called by the AssetCatalog when an asset has been modified
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetAdded(const AZ::Data::AssetId& /*assetId*/) override;
//! Called by the AssetProcessor when an asset in the cache has been removed.
void OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo) override;
////
// NodePaletteModelNotificationBus
void OnAssetModelRepopulated() override;
void OnAssetNodeAdded(NodePaletteModelInformation* modelInformation) override;
void OnAssetNodeRemoved(NodePaletteModelInformation* modelInformation) override;
////
void OnScriptCanvasAssetClicked(const QModelIndex& modelIndex);
void showEvent(QShowEvent* showEvent) override;
public slots:
void OnSelectionCleared();
void OnItemSelected(const GraphCanvas::GraphCanvasTreeItem* treeItem);
void OnFilterUpdated(const QString& filterText);
void OnScriptCanvasAssetRowsInserted(QModelIndex modelIndex, int first, int last);
private:
void TraverseTree(QModelIndex index = QModelIndex());
void ProcessAsset(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
const NodePaletteModel& m_nodePaletteModel;
AZStd::unique_ptr<Ui::ScriptCanvasStatisticsDialog> m_ui;
NodePaletteNodeUsageRootItem* m_treeRoot;
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_scriptCanvasAssetBrowserModel;
ScriptCanvasAssetNodeUsageTreeItemRoot* m_scriptCanvasAssetTreeRoot;
GraphCanvas::GraphCanvasTreeModel* m_scriptCanvasAssetTree;
ScriptCanvasAssetNodeUsageFilterModel* m_scriptCanvasAssetFilterModel;
AZStd::unordered_map< ScriptCanvas::NodeTypeIdentifier, GraphCanvas::GraphCanvasTreeItem* > m_leafMap;
};
}
@@ -0,0 +1,583 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScriptCanvasStatisticsDialog</class>
<widget class="QDialog" name="ScriptCanvasStatisticsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>999</width>
<height>626</height>
</rect>
</property>
<property name="windowTitle">
<string>Script Canvas Statistics</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzQtComponents::TabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="nodeUsageStatistics">
<attribute name="title">
<string>Node Usage Statistics</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="opaqueResize">
<bool>true</bool>
</property>
<property name="childrenCollapsible">
<bool>true</bool>
</property>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="GraphCanvas::NodePaletteWidget" name="nodePaletteWidget" native="true">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>4</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="searchWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="scriptCanvasAssetTree">
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_9">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>3</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>General Stats for</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statDisplayName">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Something Something Something</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>24</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_6">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Total Uses:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="totalUsageCount">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>145</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>15</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Unique Graph Usages:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="uniqueGraphsCount">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>15</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Average Usage Per Graph:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="averageGraphUsages">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>2</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="indent">
<number>0</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_7">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_8">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QWidget</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::TabWidget</class>
<extends>QTabWidget</extends>
<header location="global">AzQtComponents/Components/Widgets/TabWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GraphCanvas::NodePaletteWidget</class>
<extends>QWidget</extends>
<header location="global">StaticLib/GraphCanvas/Widgets/NodePalette/NodePaletteWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,495 @@
/*
* 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 <QCompleter>
#include <QEvent>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QAction>
#include <QMenu>
#include <QMessageBox>
#include <QScopedValueRollback>
#include <QLineEdit>
#include <QTimer>
#include <QPushButton>
#include <QHeaderView>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <Data/Data.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/QtMetaTypes.h>
#include <Editor/Settings.h>
#include <Editor/Translation/TranslationHelper.h>
#include <Editor/View/Widgets/PropertyGridBus.h>
#include <Editor/Include/ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
#include <Editor/Model/UnitTestBrowserFilterModel.h>
#include <ScriptCanvas/Data/DataRegistry.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <ScriptCanvas/Assets/ScriptCanvasAssetHandler.h>
#include <ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
#include <ScriptCanvas/Bus/ScriptCanvasExecutionBus.h>
#include <ScriptCanvas/Bus/UnitTestVerificationBus.h>
#include <LyViewPaneNames.h>
#include <Editor/View/Widgets/UnitTestPanel/UnitTestDockWidget.h>
#include <Editor/View/Widgets/UnitTestPanel/ui_UnitTestDockWidget.h>
#include <Editor/View/Widgets/UnitTestPanel/moc_UnitTestDockWidget.cpp>
namespace ScriptCanvasEditor
{
/////////////////////////
// ItemButtonsDelegate
/////////////////////////
ItemButtonsDelegate::ItemButtonsDelegate(QObject* parent)
: QStyledItemDelegate(parent)
, m_editIcon(QIcon(":/ScriptCanvasEditorResources/Resources/edit_icon.png").pixmap(QSize(14, 14)))
{
}
QPoint ItemButtonsDelegate::GetEditPosition(const QStyleOptionViewItem& option) const
{
return QPoint(option.rect.right() - m_editIcon.width(), option.rect.center().y() - m_editIcon.height() / 2);
}
QPoint ItemButtonsDelegate::GetResultsPosition(const QStyleOptionViewItem& option) const
{
return QPoint(option.rect.left() + m_editIcon.width() + m_leftIconPadding, option.rect.center().y() - m_editIcon.height() / 2);
}
void ItemButtonsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyledItemDelegate::paint(painter, option, index);
if (!index.model()->index(0, 0, index).isValid() && (option.state & QStyle::State_MouseOver))
{
painter->drawPixmap(GetEditPosition(option), m_editIcon);
}
}
bool ItemButtonsDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index)
{
if (!index.model()->index(0, 0, index).isValid() && event->type() == QEvent::MouseButtonRelease)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
QRect editButtonRect = m_editIcon.rect().translated(GetEditPosition(option));
QRect resultsButtonRect = m_editIcon.rect().translated(GetResultsPosition(option));
if (editButtonRect.contains(mouseEvent->pos()))
{
Q_EMIT EditButtonClicked(index);
}
else if (resultsButtonRect.contains(mouseEvent->pos()))
{
Q_EMIT ResultsButtonClicked(index);
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
///////////////////////
// UnitTestComponent
///////////////////////
void UnitTestComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<UnitTestComponent, GraphCanvas::GraphCanvasPropertyComponent>()
->Version(0)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<UnitTestComponent>("Unit Test", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "Properties")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &UnitTestComponent::GetTitle)
;
}
}
}
AZ::Entity* UnitTestComponent::CreateUnitTestEntity()
{
AZ::Entity* entity = aznew AZ::Entity("UnitTestHelper");
entity->CreateComponent<UnitTestComponent>();
return entity;
}
UnitTestComponent::UnitTestComponent()
: m_componentTitle("UnitTest")
{
}
AZStd::string_view UnitTestComponent::GetTitle()
{
return m_componentTitle;
}
/////////////////////////
// UnitTestContextMenu
/////////////////////////
UnitTestContextMenu::UnitTestContextMenu(UnitTestDockWidget* dockWidget, AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* sourceEntry)
: QMenu()
{
AZ::Uuid sourceUuid = sourceEntry->GetSourceUuid();
AZStd::string sourceDisplayName = sourceEntry->GetDisplayName().toUtf8().data();
if (dockWidget->widgetActive)
{
QAction* runAction = new QAction(QObject::tr("Run this test"), this);
runAction->setToolTip(QObject::tr("Run this Test only."));
runAction->setStatusTip(QObject::tr("Run this Test only."));
QObject::connect(runAction,
&QAction::triggered,
[dockWidget, sourceUuid]()
{
AZStd::vector<AZ::Uuid> scriptUuids;
scriptUuids.push_back(sourceUuid);
dockWidget->RunTests(scriptUuids);
}
);
addAction(runAction);
if (dockWidget->m_filter->HasTestResults(sourceUuid))
{
QAction* consoleAction = new QAction(QObject::tr("View test results"), this);
consoleAction->setToolTip(QObject::tr("Read Console Results for this Test."));
consoleAction->setStatusTip(QObject::tr("Read Console Results for this Test."));
QObject::connect(consoleAction,
&QAction::triggered,
[dockWidget, sourceUuid, sourceDisplayName]()
{
dockWidget->OpenTestResults(sourceUuid, sourceDisplayName);
}
);
addAction(consoleAction);
}
}
QAction* openAction = new QAction(QObject::tr("Edit script"), this);
openAction->setToolTip(QObject::tr("Open this Test in the Script Canvas Editor."));
openAction->setStatusTip(QObject::tr("Open this Test in the Script Canvas Editor."));
QObject::connect(openAction,
&QAction::triggered,
[dockWidget, sourceUuid]()
{
dockWidget->OpenScriptInEditor(sourceUuid);
}
);
addAction(openAction);
}
////////////////////////
// UnitTestDockWidget
////////////////////////
UnitTestDockWidget::UnitTestDockWidget(QWidget* parent /*= nullptr*/)
: AzQtComponents::StyledDockWidget(parent)
, m_ui(new Ui::UnitTestDockWidget())
, widgetActive(true)
, m_itemButtonsDelegate(new ItemButtonsDelegate(this))
{
m_ui->setupUi(this);
UnitTestWidgetNotificationBus::Handler::BusConnect();
m_ui->searchFilter->setClearButtonEnabled(true);
QObject::connect(m_ui->searchFilter, &QLineEdit::textChanged, this, &UnitTestDockWidget::OnQuickFilterChanged);
QObject::connect(m_ui->searchFilter, &QLineEdit::returnPressed, this, &UnitTestDockWidget::OnReturnPressed);
m_filterTimer.setInterval(250);
m_filterTimer.setSingleShot(true);
m_filterTimer.stop();
QObject::connect(&m_filterTimer, &QTimer::timeout, this, &UnitTestDockWidget::UpdateSearchFilter);
m_ui->testsTree->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui->testsTree, &QWidget::customContextMenuRequested, this, &UnitTestDockWidget::OnContextMenuRequested);
connect(m_ui->closeResults, &QPushButton::clicked, this, &UnitTestDockWidget::OnCloseResultsButton);
m_filter = m_ui->testsTree->m_filter;
m_ui->testsTree->setItemDelegateForColumn(0, m_itemButtonsDelegate);
QObject::connect(m_itemButtonsDelegate, &ItemButtonsDelegate::EditButtonClicked, this, &UnitTestDockWidget::OnEditButtonClicked);
QObject::connect(m_itemButtonsDelegate, &ItemButtonsDelegate::ResultsButtonClicked, this, &UnitTestDockWidget::OnResultsButtonClicked);
if (UnitTestVerificationBus::GetTotalNumOfEventHandlers() == 0)
{
m_ui->testResultsOutput->setPlainText(QString("WARNING: Functionality of this Widget has been limited - Script Canvas Testing Gem is not loaded!"));
m_ui->runButton->setDisabled(true);
widgetActive = false;
}
else
{
m_ui->consoleOutput->hide();
connect(m_ui->runButton, &QPushButton::clicked, this, &UnitTestDockWidget::OnStartTestsButton);
connect(m_ui->testsTree, &QAbstractItemView::doubleClicked, this, &UnitTestDockWidget::OnRowDoubleClicked);
}
}
UnitTestDockWidget::~UnitTestDockWidget()
{
GraphCanvas::AssetEditorNotificationBus::Handler::BusDisconnect();
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
UnitTestWidgetNotificationBus::Handler::BusDisconnect();
delete m_itemButtonsDelegate;
}
void UnitTestDockWidget::OnCheckStateCountChange(const int count)
{
m_ui->label->setText(QString("Selected %1 test(s).").arg(count));
}
void UnitTestDockWidget::OnContextMenuRequested(const QPoint& pos)
{
QModelIndex index = m_ui->testsTree->indexAt(pos);
QModelIndex sourceIndex = m_filter->mapToSource(index);
if (sourceIndex.isValid())
{
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = static_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source)
{
UnitTestContextMenu menu(this, static_cast<AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry*>(entry));
menu.exec(m_ui->testsTree->viewport()->mapToGlobal(pos));
}
}
}
void UnitTestDockWidget::OnRowDoubleClicked(QModelIndex index)
{
QModelIndex sourceIndex = m_filter->mapToSource(index);
if (sourceIndex.isValid())
{
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = static_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source)
{
AZStd::vector<AZ::Uuid> scriptUuids;
scriptUuids.emplace_back(static_cast<AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry*>(entry)->GetSourceUuid());
RunTests(scriptUuids);
}
}
}
void UnitTestDockWidget::OnEditButtonClicked(QModelIndex index)
{
QModelIndex sourceIndex = m_filter->mapToSource(index);
if (sourceIndex.isValid())
{
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = static_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source)
{
AZ::Uuid sourceUuid = static_cast<AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry*>(entry)->GetSourceUuid();
OpenScriptInEditor(sourceUuid);
}
}
}
void UnitTestDockWidget::OnResultsButtonClicked(QModelIndex index)
{
QModelIndex sourceIndex = m_filter->mapToSource(index);
if (sourceIndex.isValid())
{
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = static_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source)
{
AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* sourceEntry = static_cast<AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry*>(entry);
AZ::Uuid sourceUuid = sourceEntry->GetSourceUuid();
AZStd::string sourceDisplayName = sourceEntry->GetDisplayName().toUtf8().data();
OpenTestResults(sourceUuid, sourceDisplayName);
}
}
}
void UnitTestDockWidget::ClearSearchFilter()
{
{
QSignalBlocker blocker(m_ui->searchFilter);
m_ui->searchFilter->setText("");
}
UpdateSearchFilter();
}
void UnitTestDockWidget::UpdateSearchFilter()
{
m_ui->testsTree->SetSearchFilter(m_ui->searchFilter->userInputText());
}
void UnitTestDockWidget::OnReturnPressed()
{
UpdateSearchFilter();
}
void UnitTestDockWidget::OnQuickFilterChanged(const QString& text)
{
if(text.isEmpty())
{
//If filter was cleared, update immediately
UpdateSearchFilter();
return;
}
m_filterTimer.stop();
m_filterTimer.start();
}
void UnitTestDockWidget::OnStartTestsButton()
{
AZStd::vector<AZ::Uuid> scriptUuids;
m_filter->GetCheckedScriptsUuidsList(scriptUuids);
ClearSearchFilter();
RunTests(scriptUuids);
}
void UnitTestDockWidget::OnCloseResultsButton()
{
m_ui->consoleOutput->hide();
}
void UnitTestDockWidget::OpenScriptInEditor(AZ::Uuid sourceUuid)
{
AzToolsFramework::OpenViewPane(LyViewPane::ScriptCanvas);
AZ::Data::AssetId sourceAssetId(sourceUuid, 0);
AZ::Outcome<int, AZStd::string> openOutcome = AZ::Failure(AZStd::string());
GeneralRequestBus::BroadcastResult(openOutcome, &GeneralRequests::OpenScriptCanvasAssetId, sourceAssetId);
if (!openOutcome)
{
AZ_Warning("Script Canvas", openOutcome, "%s", openOutcome.GetError().data());
}
}
void UnitTestDockWidget::OpenTestResults(AZ::Uuid sourceUuid, AZStd::string_view sourceDisplayName)
{
if (m_filter->HasTestResults(sourceUuid))
{
m_ui->testResultsLabel->setText(QString("Test Results | %1").arg(sourceDisplayName.data()));
m_ui->testResultsOutput->setPlainText(QString(m_filter->GetTestResult(sourceUuid)->m_consoleOutput.c_str()));
m_ui->consoleOutput->show();
}
}
void UnitTestDockWidget::RunTests(const AZStd::vector<AZ::Uuid>& scriptUuids)
{
m_ui->consoleOutput->hide();
m_filter->FlushLatestTestRun();
m_filter->TestsStart();
int successCount = 0;
int failureCount = 0;
m_ui->label->setText(QString("Starting %1 tests.").arg(scriptUuids.size()));
for (const AZ::Uuid& scriptUuid : scriptUuids)
{
const SourceAssetBrowserEntry* sourceBrowserEntry = SourceAssetBrowserEntry::GetSourceByUuid(scriptUuid);
if (sourceBrowserEntry == nullptr)
{
++failureCount;
}
else
{
AZStd::string scriptAbsolutePath = sourceBrowserEntry->GetFullPath();
Reporter reporter;
UnitTestWidgetNotificationBus::Broadcast(&UnitTestWidgetNotifications::OnTestStart, scriptUuid);
ScriptCanvasExecutionBus::BroadcastResult(reporter, &ScriptCanvasExecutionRequests::RunGraph, scriptAbsolutePath);
UnitTestResult testResult;
UnitTestVerificationBus::BroadcastResult(testResult, &UnitTestVerificationRequests::Verify, reporter);
UnitTestWidgetNotificationBus::Broadcast(&UnitTestWidgetNotifications::OnTestResult, scriptUuid, testResult);
if (testResult.m_success)
{
++successCount;
}
else
{
++failureCount;
}
}
QString executionMessage = QString("Executed %1 out of %2 test(s) - ").arg(successCount + failureCount).arg(scriptUuids.size());
if (successCount > 0)
{
executionMessage += QString("%1 passed").arg(successCount);
}
if (successCount > 0 && failureCount > 0)
{
executionMessage += ", ";
}
if (failureCount > 0)
{
executionMessage += QString("%1 failed").arg(failureCount);
}
executionMessage += ".";
m_ui->label->setText(QString(executionMessage));
}
m_filter->TestsEnd();
}
}
@@ -0,0 +1,175 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QAbstractListModel>
#include <QAbstractItemView>
#include <QListView>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QTimer>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QFocusEvent>
#include <QMenu>
#include <QStyledItemDelegate>
#include <QPainter>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <GraphCanvas/Editor/AssetEditorBus.h>
#include <GraphCanvas/Components/GraphCanvasPropertyBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeModel.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/Bus/UnitTestVerificationBus.h>
#endif
class QAction;
class QLineEdit;
class QPushButton;
namespace Ui
{
class UnitTestDockWidget;
}
namespace AzToolsFramework
{
namespace AssetBrowser
{
class SourceAssetBrowserEntry;
}
}
namespace ScriptCanvasEditor
{
class ItemButtonsDelegate
: public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ItemButtonsDelegate(QObject* parent = nullptr);
ItemButtonsDelegate(const ItemButtonsDelegate&) = delete;
ItemButtonsDelegate& operator= (const ItemButtonsDelegate&) = delete;
void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const override;
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override;
Q_SIGNALS:
void EditButtonClicked(QModelIndex);
void ResultsButtonClicked(QModelIndex);
private:
QPoint GetEditPosition(const QStyleOptionViewItem& option) const;
QPoint GetResultsPosition(const QStyleOptionViewItem& option) const;
QPixmap m_editIcon;
static const int m_leftIconPadding = 9;
};
class UnitTestComponent
: public GraphCanvas::GraphCanvasPropertyComponent
{
public:
AZ_COMPONENT(UnitTestComponent, "{D4C073E6-DBFA-48A0-8B43-0A699A6CE293}", GraphCanvasPropertyComponent);
static void Reflect(AZ::ReflectContext*);
static AZ::Entity* CreateUnitTestEntity();
UnitTestComponent();
~UnitTestComponent() override = default;
AZStd::string_view GetTitle();
private:
AZStd::string m_componentTitle;
};
class UnitTestDockWidget;
class UnitTestBrowserFilterModel;
class UnitTestContextMenu
: public QMenu
{
public:
UnitTestContextMenu(UnitTestDockWidget* dockWidget, AzToolsFramework::AssetBrowser::SourceAssetBrowserEntry* sourceEntry);
};
class UnitTestDockWidget
: public AzQtComponents::StyledDockWidget
, public GraphCanvas::AssetEditorNotificationBus::Handler
, public AzToolsFramework::EditorEvents::Bus::Handler
, public UnitTestWidgetNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(UnitTestDockWidget, AZ::SystemAllocator, 0);
UnitTestDockWidget(QWidget* parent = nullptr);
~UnitTestDockWidget();
// ScriptCanvasEditor::UnitTestWidgetNotificationBus
void OnCheckStateCountChange(const int count) override;
////
friend class UnitTestContextMenu;
public Q_SLOTS:
void OnContextMenuRequested(const QPoint &pos);
void OnRowDoubleClicked(QModelIndex index);
void OnEditButtonClicked(QModelIndex index);
void OnResultsButtonClicked(QModelIndex index);
private:
void ClearSearchFilter();
void UpdateSearchFilter();
void OnReturnPressed();
void OnQuickFilterChanged(const QString &text);
void OnStartTestsButton();
void OnCloseResultsButton();
void OpenScriptInEditor(AZ::Uuid sourceUuid);
void OpenTestResults(AZ::Uuid sourceUuid, AZStd::string_view sourceDisplayName);
void RunTests(const AZStd::vector<AZ::Uuid>& scriptUuids);
AZ::EntityId m_scriptCanvasGraphId;
AZ::EntityId m_graphCanvasGraphId;
bool widgetActive;
AZStd::unique_ptr<Ui::UnitTestDockWidget> m_ui;
UnitTestBrowserFilterModel* m_filter;
ItemButtonsDelegate* m_itemButtonsDelegate;
QTimer m_filterTimer;
};
}
@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UnitTestDockWidget</class>
<widget class="QDockWidget" name="UnitTestDockWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>603</width>
<height>698</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>182</width>
<height>332</height>
</size>
</property>
<property name="windowTitle">
<string>Test Manager</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="runButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Run Selected Tests</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="AzQtComponents::SearchLineEdit" name="searchFilter">
<property name="placeholderText">
<string>Search...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="unitTestFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="ScriptCanvasEditor::UnitTestTreeView" name="testsTree">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>false</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="consoleOutput">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="testResultsLabel">
<property name="text">
<string>Test Results</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignRight">
<widget class="QPushButton" name="closeResults">
<property name="toolTip">
<string>Close Frame</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/lineedit_clear.png</normaloff>:/ScriptCanvasEditorResources/Resources/lineedit_clear.png</iconset>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="testResultsOutput">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="labelFrame">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>Selected 0 test(s).</string>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ScriptCanvasEditor::UnitTestTreeView</class>
<extends>QTreeView</extends>
<header location="global">Editor/View/Widgets/UnitTestPanel/UnitTestTreeView.h</header>
</customwidget>
<customwidget>
<class>AzQtComponents::SearchLineEdit</class>
<extends>QLineEdit</extends>
<header location="global">AzQtComponents/Components/SearchLineEdit.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../Windows/ScriptCanvasEditorResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,122 @@
/*
* 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 <qaction.h>
#include <qevent.h>
#include <qheaderview.h>
#include <qitemselectionmodel.h>
#include <qscrollbar.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzCore/Script/ScriptAsset.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <GraphCanvas/Types/TranslationTypes.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/View/Widgets/UnitTestPanel/UnitTestTreeView.h>
#include <Editor/View/Widgets/UnitTestPanel/moc_UnitTestTreeView.cpp>
#include <Editor/View/Dialogs/ContainerWizard/ContainerWizard.h>
#include <Editor/Settings.h>
#include <Editor/Translation/TranslationHelper.h>
#include <Editor/QtMetaTypes.h>
#include <ScriptCanvas/Data/DataRegistry.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzToolsFramework/Asset/AssetSystemComponent.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <Editor/Model/UnitTestBrowserFilterModel.h>
namespace ScriptCanvasEditor
{
using namespace AzToolsFramework::AssetBrowser;
//////////////////////
// UnitTestTreeView
//////////////////////
UnitTestTreeView::UnitTestTreeView(QWidget* parent)
: AzToolsFramework::QTreeViewWithStateSaving(parent)
, m_filter(new UnitTestBrowserFilterModel(parent))
{
AssetBrowserComponentRequestBus::BroadcastResult(m_model, &AssetBrowserComponentRequests::GetAssetBrowserModel);
if (!m_model)
{
AZ_Error("ScriptCanvas", false, "Unable to setup UnitTest TreeView, asset browser model was not provided.");
}
else
{
m_filter->setSourceModel(m_model);
m_filter->FilterSetup();
setModel(m_filter);
QAbstractItemView::setIconSize(QSize(14, 14));
setMouseTracking(true);
}
}
UnitTestTreeView::~UnitTestTreeView()
{
}
void UnitTestTreeView::SetSearchFilter(const QString& filter)
{
clearSelection();
m_filter->SetSearchFilter(filter);
if (!filter.isEmpty())
{
expandAll();
}
}
void UnitTestTreeView::mouseMoveEvent(QMouseEvent* event)
{
QModelIndex index = indexAt(event->pos());
QModelIndex sourceIndex = m_filter->mapToSource(index);
if (sourceIndex.isValid())
{
m_filter->SetHoveredIndex(sourceIndex);
}
else
{
m_filter->SetHoveredIndex(QModelIndex());
}
AzToolsFramework::QTreeViewWithStateSaving::mouseMoveEvent(event);
}
void UnitTestTreeView::leaveEvent([[maybe_unused]] QEvent* ev)
{
m_filter->SetHoveredIndex(QModelIndex());
}
}
@@ -0,0 +1,66 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QCompleter>
#include <QAbstractItemModel>
#include <QRegExp>
#include <QString>
#include <QSortFilterProxyModel>
#include <QTreeView>
#include <AzCore/Component/EntityId.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <ScriptCanvas/Data/Data.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx>
#include <GraphCanvas/Widgets/StyledItemDelegates/IconDecoratedNameDelegate.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#endif
namespace AzToolsFramework
{
namespace AssetBrowser
{
class AssetBrowserModel;
}
}
namespace ScriptCanvasEditor
{
class UnitTestBrowserFilterModel;
struct UnitTestResult;
class UnitTestTreeView
: public AzToolsFramework::QTreeViewWithStateSaving
{
Q_OBJECT
public:
UnitTestTreeView(QWidget* parent);
~UnitTestTreeView();
void SetSearchFilter(const QString& filter);
friend class UnitTestDockWidget;
protected:
void mouseMoveEvent(QMouseEvent* event) Q_DECL_OVERRIDE;
void leaveEvent(QEvent* ev) Q_DECL_OVERRIDE;
private:
AzToolsFramework::AssetBrowser::AssetBrowserModel* m_model;
UnitTestBrowserFilterModel* m_filter;
};
}
@@ -0,0 +1,252 @@
/*
* 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
#if !defined(Q_MOC_RUN)
#include <QAbstractItemModel>
#include <QIcon>
#include <QSortFilterProxyModel>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <GraphCanvas/Editor/AssetEditorBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/ToastBus.h>
#include <ScriptCanvas/Debugger/ValidationEvents/ValidationEvent.h>
#include <ScriptCanvas/Debugger/StatusBus.h>
#endif
namespace Ui
{
class GraphValidationPanel;
}
namespace ScriptCanvas
{
class ScopedDataConnectionEvent;
class InvalidVariableTypeEvent;
class ScriptEventVersionMismatch;
}
namespace ScriptCanvasEditor
{
class ValidationEffect
{
public:
AZ_CLASS_ALLOCATOR(ValidationEffect, AZ::SystemAllocator, 0);
virtual ~ValidationEffect() = default;
virtual void DisplayEffect(const GraphCanvas::GraphId& graphId) = 0;
virtual void CancelEffect() = 0;
};
class HighlightElementValidationEffect
: public ValidationEffect
{
public:
AZ_CLASS_ALLOCATOR(HighlightElementValidationEffect, AZ::SystemAllocator, 0);
HighlightElementValidationEffect();
HighlightElementValidationEffect(const QColor& color);
HighlightElementValidationEffect(const GraphCanvas::SceneMemberGlowOutlineConfiguration& glowConfiguration);
void AddTarget(const AZ::EntityId& targetId);
void DisplayEffect(const GraphCanvas::GraphId& graphId) override;
void CancelEffect() override;
private:
AZStd::vector< AZ::EntityId > m_targets;
GraphCanvas::GraphId m_graphId;
AZStd::vector< GraphCanvas::GraphicsEffectId > m_graphicEffectIds;
GraphCanvas::SceneMemberGlowOutlineConfiguration m_templateConfiguration;
};
class UnusedNodeValidationEffect
: public ValidationEffect
{
public:
void AddUnusedNode(const AZ::EntityId& graphCanvasNodeId);
void RemoveUnusedNode(const AZ::EntityId& graphCanvasNodeId);
void DisplayEffect(const GraphCanvas::GraphId& graphId) override;
void CancelEffect() override;
public:
void ClearStyleSelectors();
void ApplySelector(const AZ::EntityId& nodeId, AZStd::string_view styleSelector);
void RemoveSelector(const AZ::EntityId& nodeId);
bool m_isDirty;
AZStd::unordered_set< AZ::EntityId > m_unprocessedIds;
AZStd::unordered_set< AZ::EntityId > m_rootUnusedNodes;
AZStd::unordered_set< AZ::EntityId > m_inactiveNodes;
AZStd::unordered_map< AZ::EntityId, AZStd::string > m_styleSelectors;
};
class GraphValidationModel
: public QAbstractItemModel
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(GraphValidationModel, AZ::SystemAllocator, 0);
enum ColumnIndex
{
IndexForce = -1,
Description,
AutoFix,
Count
};
GraphValidationModel();
~GraphValidationModel() override;
void RunValidation(const ScriptCanvas::ScriptCanvasId& scriptCanvasId);
// QAbstractItemModel
QModelIndex index(int row, int column, const QModelIndex& parent) const override;
QModelIndex parent(const QModelIndex& index = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
////
const ScriptCanvas::ValidationEvent* FindItemForIndex(const QModelIndex& index) const;
const ScriptCanvas::ValidationEvent* FindItemForRow(int row) const;
const ScriptCanvas::ValidationResults& GetValidationResults() const;
private:
ScriptCanvas::ValidationResults m_validationResults;
QIcon m_errorIcon;
QIcon m_warningIcon;
QIcon m_messageIcon;
QIcon m_autoFixIcon;
};
class GraphValidationSortFilterProxyModel
: public QSortFilterProxyModel
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(GraphValidationSortFilterProxyModel, AZ::SystemAllocator, 0);
GraphValidationSortFilterProxyModel();
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
void SetFilter(const QString& filterString);
void SetSeverityFilter(ScriptCanvas::ValidationSeverity severityFilter);
ScriptCanvas::ValidationSeverity GetSeverityFilter() const;
bool IsShowingErrors();
bool IsShowingWarnings();
private:
ScriptCanvas::ValidationSeverity m_severityFilter;
QString m_filter;
QRegExp m_regex;
};
class GraphValidationDockWidget
: public AzQtComponents::StyledDockWidget
, public GraphCanvas::AssetEditorNotificationBus::Handler
, public GraphCanvas::SceneNotificationBus::Handler
, public GraphCanvas::ToastNotificationBus::MultiHandler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(GraphValidationDockWidget, AZ::SystemAllocator, 0);
GraphValidationDockWidget(QWidget* parent = nullptr);
~GraphValidationDockWidget();
// GraphCanvas::AssetEditorNotificationBus::Handler
void OnActiveGraphChanged(const GraphCanvas::GraphId& graphCanvasGraphId) override;
////
// GrpahCanvas::SceneNotificationBus
void OnSelectionChanged() override;
void OnConnectionDragBegin() override;
////
// ToastNotification
void OnToastInteraction() override;
void OnToastDismissed() override;
////
bool HasValidationIssues() const;
public slots:
void OnRunValidator(bool displayAsNotification = false);
void OnShowErrors();
void OnShowWarnings();
void OnTableSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void FocusOnEvent(const QModelIndex& modelIndex);
void TryAutoFixEvent(const QModelIndex& modelIndex);
void FixSelected();
void OnSeverityFilterChanged();
void OnFilterChanged(const QString& filterString);
private:
// AutoFixes
void AutoFixEvent(const ScriptCanvas::ValidationEvent* validationEvent);
void AutoFixScopedDataConnection(const ScriptCanvas::ScopedDataConnectionEvent* connectionEvent);
void AutoFixDeleteInvalidVariables(const ScriptCanvas::InvalidVariableTypeEvent* invalidVariableEvent);
void AutoFixScriptEventVersionMismatch(const ScriptCanvas::ScriptEventVersionMismatch* scriptEventMismatchEvent);
////
void UpdateText();
void OnRowSelected(int row);
void OnRowDeselected(int row);
void UpdateSelectedText();
GraphValidationModel* m_model;
GraphValidationSortFilterProxyModel* m_proxyModel;
ScriptCanvas::ScriptCanvasId m_scriptCanvasId;
GraphCanvas::GraphId m_graphCanvasGraphId;
UnusedNodeValidationEffect m_unusedNodeValidationEffect;
AZStd::unordered_map< int, ValidationEffect* > m_validationEffects;
AZStd::unique_ptr<Ui::GraphValidationPanel> ui;
};
}
@@ -0,0 +1,30 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <GraphCanvas/Editor/EditorTypes.h>
namespace ScriptCanvasEditor
{
class GraphValidatorDockWidgetNotifications : public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = GraphCanvas::EditorId;
virtual void OnResultsChanged(int erorrCount, int warningCount) = 0;
};
using GraphValidatorDockWidgetNotificationBus = AZ::EBus<GraphValidatorDockWidgetNotifications>;
}
@@ -0,0 +1,336 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GraphValidationPanel</class>
<widget class="QDockWidget" name="GraphValidationPanel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>307</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>694</width>
<height>175</height>
</size>
</property>
<property name="allowedAreas">
<set>Qt::AllDockWidgetAreas</set>
</property>
<property name="windowTitle">
<string>Graph Validation</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>710</width>
<height>242</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="allFilter">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>All</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="errorOnlyFilter">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>999 Errors</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/error_icon.png</normaloff>:/ScriptCanvasEditorResources/Resources/error_icon.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="warningOnlyFilter">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>999 Warnings</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/warning_symbol.png</normaloff>:/ScriptCanvasEditorResources/Resources/warning_symbol.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="runValidation">
<property name="toolTip">
<string>Will run a validation check on the current graph and report any warnings/errors discovered.</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/validate_icon.png</normaloff>:/ScriptCanvasEditorResources/Resources/validate_icon.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="searchWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>400</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTableView" name="statusTableView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="fixSelectedText">
<property name="text">
<string>999 Selected</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fixSelected">
<property name="text">
<string>Fix Selected</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QWidget</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../Windows/ScriptCanvasEditorResources.qrc"/>
</resources>
<connections/>
</ui>

Some files were not shown because too many files have changed in this diff Show More