Files
o3de/Gems/ScriptCanvas/Code/Editor/View/Windows/MainWindow.cpp
T
lumberyard-employee-dm 3dec5d3b71 LYN-2537 engine assets (#254)
* LYN-2537 Moved the Engine and Editor folder to be within the EngineAssets folder

* Fixed Documentation in bootstrap.cfg to correct the path to the user project specific registry file

* Adding a newline to the output of AssetCatalog 'Registering asset..., but type is not set' message

* Updating the AssetProcessorPlatformConfig.setreg Scan Folder to detect
the @ENGINEROOT@/EngineAssets/Engine path for engine runtime assets and
@ENGINEROOT@/EngineAssets/Editor path for engine tool assets

* Updating references to Icons and other assets to account for moving the
Engine and Editor folder under a single EngineAssets folder

* Moving the Engine Settings Registry folder from Engine/Registry -> Registry

* Removed the LY_PROJECT_CMAKE_PATH define as it is not portable to other locations. It is hard coded to the project location that was used for the CMake configuration. Furthermore it paths with backslashes within it are treated as escape characters and not a path separator

* Updated the LyTestTools asset_processor.py script to copy the exclude.filetag from the EngineAssets/Engine directory now

* Fixed Atom Shader Preprocessing when running using an External Project

* Updated the TSGenerateAction.cpp to fix the build error with using a renamed variable

* Updated the Install_Common.cmake ly_setup_others function to install the
EngineAssets directory and the each of the Gem's Assets directory while
maintaining the relative directory structure to the Engine Root
Also updated the install step to install the Registry folder at the
engine root

* Fixed the copying of the Registry folder to be in the install root, instead of under a second 'Registry' folder

* Moving the AssetProcessorPlatformConfig.setreg file over to the Registry folder

* Updated the LyTestTools and C++ code to point that the new location of
the AssetProcessorPlatformConfig.setreg file inside of the Registry
folder

* Renamed Test AssetProcessor*Config.ini files to have the .setreg extension

* Converted the AssetProcessor test setreg files from ini format to json
format using the SerializeContextTools convert-ini command

* Updated the AssetProcessor CMakeLists.txt to copy over the test setreg files to the build folder

* Updated the assetprocessor test file list to point at the renamed AsssetProcessor*Config setreg filenames

* Removed the Output Prefix code from the AssetProcessor. The complexity that it brought to the AP code is not needed, as users can replicate the behavior by just moving there assets underneath a another folder, underneath the scan folder

* Adding back support to read the AssetProcessorPlatformConfig.setreg file from the asset root. This is only needed for C++ UnitTests as they run in an environment where the accessing the Engine Settings Registry is not available

* Updating the Install_common.cmake logic to copy any "Assets" folder to
the install layout.
The Script has also been updated to copy over the "Assets" folder in the
Engine Root to the install layout instead of an "EngineAssets" folder

* Updating References to EngineAssets source asset folder in code to be the Assets source folder

* Moved the Engine Source Asset folder of 'EngineAssets' to a new folder name of 'Assets'. This is inline with the naming scheme we use for Gem asset folders

* Adding the EngineFinder.cmake to the AutomatedTesting project to allow it to work in a project centric manner

* Updating the LyTestTools copy_assets_to_project function to be able to copy assets with folders to the temporary project root
Fixed an issue in LyTestTools where the temporary log directory could have shutil.rmtree being called twice on it leading to an exception which fails an automated test

Updated the asset_procesor_gui_tests_2 AddScanFolder test to not use the
output prefix, but instead place the source asset root into a
subdirectory

* Correct the AssetProcessorPlatformConfig Scan Folders for the EngineAssets directory to point at the Assets directory

* Updated the asset procesor batch dependency test scan folder to point at the 'Assets' folder instead of 'EngineAssets'
2021-04-28 21:38:43 -05:00

4929 lines
182 KiB
C++

/*
* 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 <ISystem.h>
#include <IConsole.h>
#include <Editor/View/Windows/MainWindow.h>
#include <Editor/GraphCanvas/AutomationIds.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <QSplitter>
#include <QListView>
#include <QFileDialog>
#include <QShortcut>
#include <QKeySequence>
#include <QKeyEvent>
#include <QApplication>
#include <QClipboard>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsSceneEvent>
#include <QMimeData>
#include <QCoreApplication>
#include <QMessageBox>
#include <QDir>
#include <QDirIterator>
#include <QProgressDialog>
#include <QToolButton>
#include <ScriptEvents/ScriptEventsAsset.h>
#include <Editor/GraphCanvas/Components/MappingComponent.h>
#include <Editor/View/Dialogs/UnsavedChangesDialog.h>
#include <Editor/View/Dialogs/SettingsDialog.h>
#include <Editor/View/Widgets/ScriptCanvasNodePaletteDockWidget.h>
#include <Editor/View/Widgets/PropertyGrid.h>
#include <Editor/View/Widgets/CommandLine.h>
#include <Editor/View/Widgets/GraphTabBar.h>
#include <Editor/View/Widgets/CanvasWidget.h>
#include <Editor/View/Widgets/LogPanel.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindow.h>
#include <Editor/View/Widgets/MainWindowStatusWidget.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <Editor/View/Widgets/StatisticsDialog/ScriptCanvasStatisticsDialog.h>
#include <Editor/View/Widgets/VariablePanel/VariableDockWidget.h>
#include <Editor/View/Widgets/UnitTestPanel/UnitTestDockWidget.h>
#include <Editor/View/Widgets/ValidationPanel/GraphValidationDockWidget.h>
#include <Editor/View/Windows/ui_mainwindow.h>
#include <Editor/Model/EntityMimeDataHandler.h>
#include <Editor/Utilities/RecentAssetPath.h>
#include <Editor/Settings.h>
#include <Editor/Nodes/NodeCreateUtils.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Component/EntityUtils.h>
#include <AzCore/Serialization/IdUtils.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzFramework/Asset/AssetCatalog.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EntityCompositionRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/ToolsComponents/EditorEntityIdContainer.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
#include <AzToolsFramework/ToolsComponents/ToolsAssetCatalogBus.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <AzQtComponents/Components/Widgets/TabWidget.h>
#include <ScriptCanvas/Core/ScriptCanvasBus.h>
#include <ScriptCanvas/Core/Graph.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <ScriptCanvas/Assets/ScriptCanvasAssetHandler.h>
#include <ScriptCanvas/Libraries/Core/FunctionDefinitionNode.h>
#include <GraphCanvas/GraphCanvasBus.h>
#include <GraphCanvas/Components/Nodes/NodeBus.h>
#include <GraphCanvas/Components/GeometryBus.h>
#include <GraphCanvas/Components/GridBus.h>
#include <GraphCanvas/Components/ViewBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/Components/MimeDataHandlerBus.h>
#include <GraphCanvas/Components/Connections/ConnectionBus.h>
#include <GraphCanvas/Styling/Parser.h>
#include <GraphCanvas/Styling/Style.h>
#include <GraphCanvas/Widgets/AssetEditorToolbar/AssetEditorToolbar.h>
#include <GraphCanvas/Widgets/Bookmarks/BookmarkDockWidget.h>
#include <GraphCanvas/Widgets/GraphCanvasMimeContainer.h>
#include <GraphCanvas/Widgets/MiniMapGraphicsView/MiniMapGraphicsView.h>
#include <GraphCanvas/Widgets/GraphCanvasEditor/GraphCanvasEditorCentralWidget.h>
#include <GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h>
#include <GraphCanvas/Widgets/EditorContextMenu/EditorContextMenu.h>
#include <GraphCanvas/Widgets/EditorContextMenu/ContextMenus/BookmarkContextMenu.h>
#include <GraphCanvas/Widgets/EditorContextMenu/ContextMenus/CollapsedNodeGroupContextMenu.h>
#include <GraphCanvas/Widgets/EditorContextMenu/ContextMenus/ConnectionContextMenu.h>
#include <GraphCanvas/Widgets/EditorContextMenu/ContextMenus/NodeGroupContextMenu.h>
#include <GraphCanvas/Widgets/EditorContextMenu/ContextMenus/NodeContextMenu.h>
#include <GraphCanvas/Widgets/EditorContextMenu/ContextMenus/CommentContextMenu.h>
#include <GraphCanvas/Widgets/EditorContextMenu/ContextMenus/SceneContextMenu.h>
#include <GraphCanvas/Widgets/EditorContextMenu/ContextMenus/SlotContextMenu.h>
#include <GraphCanvas/Utils/ConversionUtils.h>
#include <GraphCanvas/Utils/NodeNudgingController.h>
#include <GraphCanvas/Types/ConstructPresets.h>
#include <Editor/View/Windows/ScriptCanvasContextMenus.h>
#include <Editor/View/Windows/EBusHandlerActionMenu.h>
#include <Editor/View/Widgets/NodePalette/CreateNodeMimeEvent.h>
#include <Editor/View/Widgets/NodePalette/EBusNodePaletteTreeItemTypes.h>
#include <Editor/View/Windows/Tools/UpgradeTool/UpgradeHelper.h>
#include <Editor/View/Windows/Tools/UpgradeTool/VersionExplorer.h>
#include <Editor/View/Widgets/VariablePanel/SlotTypeSelectorWidget.h>
// Save Format Conversion
#include <AzCore/Component/EntityUtils.h>
#include <Editor/Include/ScriptCanvas/Components/EditorGraph.h>
////
#include <Editor/Assets/ScriptCanvasAssetHelpers.h>
#include <Editor/Assets/ScriptCanvasAssetTracker.h>
#include <Editor/Assets/ScriptCanvasAssetTrackerDefinitions.h>
#include <ScriptCanvas/Asset/AssetDescription.h>
#include <ScriptCanvas/Components/EditorScriptCanvasComponent.h>
#include <ScriptCanvas/Assets/Functions/ScriptCanvasFunctionAssetHandler.h>
#include <ScriptCanvas/Asset/Functions/ScriptCanvasFunctionAsset.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <Editor/QtMetaTypes.h>
#include <GraphCanvas/Components/SceneBus.h>
namespace ScriptCanvasEditor
{
using namespace AzToolsFramework;
namespace
{
template <typename T>
class ScopedVariableSetter
{
public:
ScopedVariableSetter(T& value)
: m_oldValue(value)
, m_value(value)
{
}
ScopedVariableSetter(T& value, const T& newValue)
: m_oldValue(value)
, m_value(value)
{
m_value = newValue;
}
~ScopedVariableSetter()
{
m_value = m_oldValue;
}
private:
T m_oldValue;
T& m_value;
};
template<typename MimeDataDelegateHandler, typename ... ComponentArgs>
AZ::EntityId CreateMimeDataDelegate(ComponentArgs... componentArgs)
{
AZ::Entity* mimeDelegateEntity = aznew AZ::Entity("MimeData Delegate");
mimeDelegateEntity->CreateComponent<MimeDataDelegateHandler>(AZStd::forward<ComponentArgs>(componentArgs) ...);
mimeDelegateEntity->Init();
mimeDelegateEntity->Activate();
return mimeDelegateEntity->GetId();
}
void EnsureSaveDestinationDirectory(AZStd::string directoryLocation)
{
if (AzFramework::StringFunc::Path::HasExtension(directoryLocation.c_str()))
{
size_t offset = directoryLocation.find_last_of('/');
if (offset != AZStd::string::npos)
{
directoryLocation = directoryLocation.substr(0, offset);
}
else
{
AzFramework::StringFunc::Path::StripComponent(directoryLocation, true);
}
}
// We just need the path to exist.
QDir canvasDirectory = QDir(directoryLocation.c_str());
if (!canvasDirectory.exists())
{
canvasDirectory.mkpath(".");
}
}
} // anonymous namespace.
void Workspace::Save()
{
auto workspace = AZ::UserSettings::CreateFind<EditorSettings::EditorWorkspace>(AZ_CRC("ScriptCanvasEditorWindowState", 0x10c47d36), AZ::UserSettings::CT_LOCAL);
if (workspace)
{
workspace->Init(m_mainWindow->saveState(), m_mainWindow->saveGeometry());
Widget::GraphTabBar* tabBar = m_mainWindow->m_tabBar;
AZStd::vector<EditorSettings::EditorWorkspace::WorkspaceAssetSaveData> activeAssets;
AZ::Data::AssetId focusedAssetId = tabBar->FindAssetId(tabBar->currentIndex());
if (m_rememberOpenCanvases)
{
activeAssets.reserve(tabBar->count());
for (int i = 0; i < tabBar->count(); ++i)
{
AZ::Data::AssetId assetId = tabBar->FindAssetId(i);
const Tracker::ScriptCanvasFileState& fileState = m_mainWindow->GetAssetFileState(assetId);
if (fileState == Tracker::ScriptCanvasFileState::MODIFIED || fileState == Tracker::ScriptCanvasFileState::UNMODIFIED)
{
AZ::Data::AssetId sourceId = GetSourceAssetId(assetId);
if (sourceId.IsValid())
{
EditorSettings::EditorWorkspace::WorkspaceAssetSaveData assetSaveData;
assetSaveData.m_assetId = sourceId;
ScriptCanvas::ScriptCanvasId scriptCanvasId = m_mainWindow->FindScriptCanvasIdByAssetId(assetId);
EditorGraphRequests* editorRequests = EditorGraphRequestBus::FindFirstHandler(scriptCanvasId);
if (editorRequests)
{
if (editorRequests->IsFunctionGraph())
{
assetSaveData.m_assetType = azrtti_typeid<ScriptCanvasFunctionAsset>();
}
else
{
assetSaveData.m_assetType = azrtti_typeid<ScriptCanvasAsset>();
}
}
activeAssets.push_back(assetSaveData);
}
}
else if (assetId == focusedAssetId)
{
focusedAssetId.SetInvalid();
}
}
// The assetId needs to be the file AssetId to restore the workspace
if (focusedAssetId.IsValid())
{
focusedAssetId = GetSourceAssetId(focusedAssetId);
}
// If our currently focused asset won't be restored, just show the first element.
if (!focusedAssetId.IsValid())
{
if (!activeAssets.empty())
{
focusedAssetId = activeAssets.front().m_assetId;
}
}
}
workspace->Clear();
if (!activeAssets.empty())
{
workspace->ConfigureActiveAssets(focusedAssetId, activeAssets);
}
}
}
// Workspace
void Workspace::Restore()
{
auto workspace = AZ::UserSettings::Find<EditorSettings::EditorWorkspace>(AZ_CRC("ScriptCanvasEditorWindowState", 0x10c47d36), AZ::UserSettings::CT_LOCAL);
if (workspace)
{
workspace->Restore(qobject_cast<QMainWindow*>(m_mainWindow));
if (m_rememberOpenCanvases)
{
for (const auto& assetSaveData : workspace->GetActiveAssetData())
{
m_loadingAssets.push_back(assetSaveData.m_assetId);
}
if (m_loadingAssets.empty())
{
m_mainWindow->OnWorkspaceRestoreEnd(AZ::Data::AssetId());
}
else
{
m_mainWindow->OnWorkspaceRestoreStart();
}
AZ::Data::AssetId focusedAsset = workspace->GetFocusedAssetId();
m_queuedAssetFocus = workspace->GetFocusedAssetId();
for (const auto& assetSaveData : workspace->GetActiveAssetData())
{
AssetTrackerNotificationBus::MultiHandler::BusConnect(assetSaveData.m_assetId);
Callbacks::OnAssetReadyCallback onAssetReady = [this, focusedAsset, assetSaveData](ScriptCanvasMemoryAsset& asset)
{
// If we get an error callback. Just remove it from out active lists.
if (asset.IsSourceInError())
{
if (assetSaveData.m_assetId == m_queuedAssetFocus)
{
m_queuedAssetFocus = AZ::Data::AssetId();
}
SignalAssetComplete(asset.GetFileAssetId());
}
};
bool loadedFile = true;
AssetTrackerRequestBus::BroadcastResult(loadedFile, &AssetTrackerRequests::Load, assetSaveData.m_assetId, assetSaveData.m_assetType, onAssetReady);
if (!loadedFile)
{
if (assetSaveData.m_assetId == m_queuedAssetFocus)
{
m_queuedAssetFocus = AZ::Data::AssetId();
}
SignalAssetComplete(assetSaveData.m_assetId);
}
}
}
else
{
m_mainWindow->OnWorkspaceRestoreEnd(AZ::Data::AssetId());
}
}
}
void Workspace::OnAssetReady(const ScriptCanvasMemoryAsset::pointer memoryAsset)
{
const AZ::Data::AssetId& fileAssetId = memoryAsset->GetFileAssetId();
if (AssetTrackerNotificationBus::MultiHandler::BusIsConnectedId(fileAssetId))
{
AssetTrackerNotificationBus::MultiHandler::BusDisconnect(fileAssetId);
m_mainWindow->OpenScriptCanvasAsset(*memoryAsset);
SignalAssetComplete(fileAssetId);
}
}
void Workspace::SignalAssetComplete(const AZ::Data::AssetId& fileAssetId)
{
auto it = AZStd::find(m_loadingAssets.begin(), m_loadingAssets.end(), fileAssetId);
if (it != m_loadingAssets.end())
{
m_loadingAssets.erase(it);
}
//! When we are done loading all assets we can safely set the focus to the recorded asset
if (m_loadingAssets.empty())
{
m_mainWindow->OnWorkspaceRestoreEnd(m_queuedAssetFocus);
m_queuedAssetFocus.SetInvalid();
}
}
AZ::Data::AssetId Workspace::GetSourceAssetId(const AZ::Data::AssetId& memoryAssetId) const
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, memoryAssetId);
if (memoryAsset)
{
return memoryAsset->GetFileAssetId();
}
return AZ::Data::AssetId();
}
////////////////
// MainWindow
////////////////
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent, Qt::Widget | Qt::WindowMinMaxButtonsHint)
, ui(new Ui::MainWindow)
, m_loadingNewlySavedFile(false)
, m_isClosingTabs(false)
, m_enterState(false)
, m_ignoreSelection(false)
, m_isRestoringWorkspace(false)
, m_preventUndoStateUpdateCount(0)
, m_queueCloseRequest(false)
, m_hasQueuedClose(false)
, m_isInAutomation(false)
, m_allowAutoSave(true)
, m_systemTickActions(0)
, m_closeCurrentGraphAfterSave(false)
, m_styleManager(ScriptCanvasEditor::AssetEditorId, "ScriptCanvas/StyleSheet/graphcanvas_style.json")
{
VariablePaletteRequestBus::Handler::BusConnect();
GraphCanvas::AssetEditorAutomationRequestBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
AZStd::array<char, AZ::IO::MaxPathLength> unresolvedPath;
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@assets@/translation/scriptcanvas_en_us.qm", unresolvedPath.data(), unresolvedPath.size());
QString translationFilePath(unresolvedPath.data());
if ( m_translator.load(QLocale::Language::English, translationFilePath) )
{
if ( !qApp->installTranslator(&m_translator) )
{
AZ_Warning("ScriptCanvas", false, "Error installing translation %s!", unresolvedPath.data());
}
}
else
{
AZ_Warning("ScriptCanvas", false, "Error loading translation file %s", unresolvedPath.data());
}
AzToolsFramework::AssetBrowser::AssetBrowserModel* assetBrowserModel = nullptr;
AzToolsFramework::AssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(assetBrowserModel, &AzToolsFramework::AssetBrowser::AssetBrowserComponentRequests::GetAssetBrowserModel);
{
m_scriptEventsAssetModel = new ScriptCanvasAssetBrowserModel(this);
AzToolsFramework::AssetBrowser::AssetGroupFilter* scriptEventAssetFilter = new AzToolsFramework::AssetBrowser::AssetGroupFilter();
scriptEventAssetFilter->SetAssetGroup(ScriptEvents::ScriptEventsAsset::GetGroup());
scriptEventAssetFilter->SetFilterPropagation(AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter::PropagateDirection::Down);
m_scriptEventsAssetModel->setSourceModel(assetBrowserModel);
}
{
m_scriptCanvasAssetModel = new ScriptCanvasAssetBrowserModel(this);
AzToolsFramework::AssetBrowser::AssetGroupFilter* scriptCanvasAssetFilter = new AzToolsFramework::AssetBrowser::AssetGroupFilter();
scriptCanvasAssetFilter->SetAssetGroup(ScriptCanvasAsset::Description::GetGroup(azrtti_typeid<ScriptCanvasAsset>()));
scriptCanvasAssetFilter->SetFilterPropagation(AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter::PropagateDirection::Down);
m_scriptCanvasAssetModel->setSourceModel(assetBrowserModel);
}
m_nodePaletteModel.AssignAssetModel(m_scriptCanvasAssetModel);
ui->setupUi(this);
CreateMenus();
UpdateRecentMenu();
m_host = new QWidget();
m_layout = new QVBoxLayout();
m_emptyCanvas = aznew GraphCanvas::GraphCanvasEditorEmptyDockWidget(this);
m_emptyCanvas->SetDragTargetText(tr("Use the File Menu or drag out a node from the Node Palette to create a new script.").toStdString().c_str());
m_emptyCanvas->SetEditorId(ScriptCanvasEditor::AssetEditorId);
m_emptyCanvas->RegisterAcceptedMimeType(Widget::NodePaletteDockWidget::GetMimeType());
m_emptyCanvas->RegisterAcceptedMimeType(AzToolsFramework::EditorEntityIdContainer::GetMimeType());
m_editorToolbar = aznew GraphCanvas::AssetEditorToolbar(ScriptCanvasEditor::AssetEditorId);
// Custom Actions
{
m_assignToSelectedEntity = new QToolButton();
m_assignToSelectedEntity->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/attach_to_entity.png"));
m_assignToSelectedEntity->setToolTip("Assigns the currently active graph to all of the currently selected entities.");
m_selectedEntityMenu = new QMenu();
m_assignToSelectedEntity->setPopupMode(QToolButton::ToolButtonPopupMode::MenuButtonPopup);
m_assignToSelectedEntity->setMenu(m_selectedEntityMenu);
m_assignToSelectedEntity->setEnabled(false);
m_editorToolbar->AddCustomAction(m_assignToSelectedEntity);
QObject::connect(m_selectedEntityMenu, &QMenu::aboutToShow, this, &MainWindow::OnSelectedEntitiesAboutToShow);
QObject::connect(m_assignToSelectedEntity, &QToolButton::clicked, this, &MainWindow::OnAssignToSelectedEntities);
}
// Creation Actions
{
m_createScriptCanvas = new QToolButton();
m_createScriptCanvas->setIcon(QIcon(ScriptCanvas::AssetDescription::GetIconPath<ScriptCanvasAsset>()));
m_createScriptCanvas->setToolTip("Creates a new Script Canvas Graph");
QObject::connect(m_createScriptCanvas, &QToolButton::clicked, this, &MainWindow::OnFileNew);
m_editorToolbar->AddCreationAction(m_createScriptCanvas);
RegisterObject(AutomationIds::CreateScriptCanvasButton, m_createScriptCanvas);
}
{
m_createFunctionInput = new QToolButton();
m_createFunctionInput->setToolTip("Creates an Execution Nodeling on the leftmost side of the graph to be used as input for the graph.");
m_createFunctionInput->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/create_function_input.png"));
m_createFunctionInput->setEnabled(false);
}
m_editorToolbar->AddCustomAction(m_createFunctionInput);
connect(m_createFunctionInput, &QToolButton::clicked, this, &MainWindow::CreateFunctionInput);
{
m_createFunctionOutput = new QToolButton();
m_createFunctionOutput->setToolTip("Creates an Execution Nodeling on the rightmost side of the graph to be used as output for the graph.");
m_createFunctionOutput->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/create_function_output.png"));
m_createFunctionOutput->setEnabled(false);
}
m_editorToolbar->AddCustomAction(m_createFunctionOutput);
connect(m_createFunctionOutput, &QToolButton::clicked, this, &MainWindow::CreateFunctionOutput);
{
m_validateGraphToolButton = new QToolButton();
m_validateGraphToolButton->setToolTip("Will run a validation check on the current graph and report any warnings/errors discovered.");
m_validateGraphToolButton->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/validate_icon.png"));
m_validateGraphToolButton->setEnabled(false);
}
m_editorToolbar->AddCustomAction(m_validateGraphToolButton);
connect(m_validateGraphToolButton, &QToolButton::clicked, this, &MainWindow::OnValidateCurrentGraph);
m_layout->addWidget(m_editorToolbar);
// Tab bar
{
m_tabWidget = new AzQtComponents::TabWidget(m_host);
m_tabBar = new Widget::GraphTabBar(m_tabWidget);
m_tabWidget->setCustomTabBar(m_tabBar);
m_tabWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
connect(m_tabBar, &QTabBar::tabCloseRequested, this, &MainWindow::OnTabCloseButtonPressed);
connect(m_tabBar, &Widget::GraphTabBar::TabCloseNoButton, this, &MainWindow::OnTabCloseRequest);
connect(m_tabBar, &Widget::GraphTabBar::SaveTab, this, &MainWindow::SaveTab);
connect(m_tabBar, &Widget::GraphTabBar::CloseAllTabsSignal, this, &MainWindow::CloseAllTabs);
connect(m_tabBar, &Widget::GraphTabBar::CloseAllTabsButSignal, this, &MainWindow::CloseAllTabsBut);
connect(m_tabBar, &Widget::GraphTabBar::CopyPathToClipboard, this, &MainWindow::CopyPathToClipboard);
connect(m_tabBar, &Widget::GraphTabBar::OnActiveFileStateChanged, this, &MainWindow::OnActiveFileStateChanged);
AzQtComponents::TabWidget::applySecondaryStyle(m_tabWidget, false);
m_tabWidget->setObjectName("ScriptCanvasTabs");
m_layout->addWidget(m_tabWidget);
}
m_commandLine = new Widget::CommandLine(this);
m_commandLine->setBaseSize(QSize(size().width(), m_commandLine->size().height()));
m_commandLine->setObjectName("CommandLine");
m_layout->addWidget(m_commandLine);
m_layout->addWidget(m_emptyCanvas);
// Minimap should be a child of the dock widget. But until performance concerns are resolved
// we want to hide it(mostly to avoid re-setting up all of the structural code around it).
//
// If this is a child, it appears on the default context menu to show/hide.
m_minimap = aznew GraphCanvas::MiniMapDockWidget(ScriptCanvasEditor::AssetEditorId);
m_minimap->setObjectName("MiniMapDockWidget");
m_statusWidget = aznew MainWindowStatusWidget(this);
statusBar()->addWidget(m_statusWidget,1);
QObject::connect(m_statusWidget, &MainWindowStatusWidget::OnErrorButtonPressed, this, &MainWindow::OnShowValidationErrors);
QObject::connect(m_statusWidget, &MainWindowStatusWidget::OnWarningButtonPressed, this, &MainWindow::OnShowValidationWarnings);
m_nodePaletteModel.RepopulateModel();
// Order these are created denotes the order for an auto-generate Qt menu. Keeping this construction order
// in sync with the order we display under tools for consistency.
{
const bool isInContextMenu = false;
Widget::ScriptCanvasNodePaletteConfig nodePaletteConfig(m_nodePaletteModel, m_scriptEventsAssetModel, isInContextMenu);
m_nodePalette = aznew Widget::NodePaletteDockWidget(tr("Node Palette"), this, nodePaletteConfig);
m_nodePalette->setObjectName("NodePalette");
RegisterObject(AutomationIds::NodePaletteDockWidget, m_nodePalette);
RegisterObject(AutomationIds::NodePaletteWidget, m_nodePalette->GetNodePaletteWidget());
}
m_propertyGrid = new Widget::PropertyGrid(this, "Node Inspector");
m_propertyGrid->setObjectName("NodeInspector");
m_bookmarkDockWidget = aznew GraphCanvas::BookmarkDockWidget(ScriptCanvasEditor::AssetEditorId, this);
m_variableDockWidget = new VariableDockWidget(this);
m_variableDockWidget->setObjectName("VariableManager");
QObject::connect(m_variableDockWidget, &VariableDockWidget::OnVariableSelectionChanged, this, &MainWindow::OnVariableSelectionChanged);
// This needs to happen after the node palette is created, because we scrape for the variable data from inside
// of there.
m_variableDockWidget->PopulateVariablePalette(m_variablePaletteTypes);
m_validationDockWidget = aznew GraphValidationDockWidget(this);
m_validationDockWidget->setObjectName("ValidationDockWidget");
// End Construction list
m_loggingWindow = aznew LoggingWindow(this);
m_loggingWindow->setObjectName("LoggingWindow");
m_ebusHandlerActionMenu = aznew EBusHandlerActionMenu();
m_statisticsDialog = aznew StatisticsDialog(m_nodePaletteModel, m_scriptCanvasAssetModel, nullptr);
m_statisticsDialog->hide();
m_presetEditor = aznew GraphCanvas::ConstructPresetDialog(nullptr);
m_presetEditor->SetEditorId(ScriptCanvasEditor::AssetEditorId);
m_presetWrapper = new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionAutoTitleBarButtons);
m_presetWrapper->setGuest(m_presetEditor);
m_presetWrapper->hide();
m_host->setLayout(m_layout);
setCentralWidget(m_host);
m_workspace = new Workspace(this);
QTimer::singleShot(0, [this]() {
SetDefaultLayout();
if (m_activeAssetId.IsValid())
{
m_queuedFocusOverride = m_activeAssetId;
}
m_workspace->Restore();
m_workspace->Save();
});
m_entityMimeDelegateId = CreateMimeDataDelegate<ScriptCanvasEditor::EntityMimeDataHandler>();
ScriptCanvasEditor::GeneralRequestBus::Handler::BusConnect();
ScriptCanvasEditor::AutomationRequestBus::Handler::BusConnect();
UIRequestBus::Handler::BusConnect();
UndoNotificationBus::Handler::BusConnect();
GraphCanvas::AssetEditorRequestBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
GraphCanvas::AssetEditorSettingsRequestBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
ScriptCanvas::BatchOperationNotificationBus::Handler::BusConnect();
AssetGraphSceneBus::Handler::BusConnect();
AzToolsFramework::ToolsApplicationNotificationBus::Handler::BusConnect();
ScriptCanvas::ScriptCanvasSettingsRequestBus::Handler::BusConnect();
UINotificationBus::Broadcast(&UINotifications::MainWindowCreationEvent, this);
m_userSettings = AZ::UserSettings::CreateFind<EditorSettings::ScriptCanvasEditorSettings>(AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL);
if (m_userSettings)
{
m_allowAutoSave = m_userSettings->m_autoSaveConfig.m_enabled;
m_showUpgradeTool = m_userSettings->m_showUpgradeDialog;
m_autoSaveTimer.setInterval(m_userSettings->m_autoSaveConfig.m_timeSeconds * 1000);
m_userSettings->m_constructPresets.SetEditorId(ScriptCanvasEditor::AssetEditorId);
}
// These should be created after we load up the user settings so we can
// initialize the user presets
m_sceneContextMenu = aznew SceneContextMenu(m_nodePaletteModel, m_scriptEventsAssetModel);
m_connectionContextMenu = aznew ConnectionContextMenu(m_nodePaletteModel, m_scriptEventsAssetModel);
connect(m_nodePalette, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
connect(m_minimap, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
connect(m_propertyGrid, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
connect(m_bookmarkDockWidget, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
connect(m_variableDockWidget, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
connect(m_loggingWindow, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
connect(m_validationDockWidget, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
m_autoSaveTimer.setSingleShot(true);
connect(&m_autoSaveTimer, &QTimer::timeout, this, &MainWindow::OnAutoSave);
UpdateMenuState(false);
PromptForUpgrade();
}
void MainWindow::PromptForUpgrade()
{
static bool displayUpgradePrompt = false; // Set to true if you need the upgrade dialog to show up on ScriptCanvas editor open
if (displayUpgradePrompt && m_showUpgradeTool)
{
QTimer::singleShot(1.f, this, [this]()
{
UpgradeTool* upgradeTool = aznew UpgradeTool(this);
QPoint centerPoint = frameGeometry().center();
upgradeTool->adjustSize();
upgradeTool->move(centerPoint.x() - upgradeTool->width() / 2, centerPoint.y() - upgradeTool->height() / 2);
if (upgradeTool->exec() == QDialog::Accepted)
{
// Manual correction
size_t assetsThatNeedManualInspection = AZ::Interface<IUpgradeRequests>::Get()->GetGraphsThatNeedManualUpgrade().size();
QString message = QObject::tr("%1 Graph(s) upgraded<br>%2 graph(s) did not require upgrade").arg(upgradeTool->UpgradedGraphCount()).arg(upgradeTool->SkippedGraphCount());
if (assetsThatNeedManualInspection > 0)
{
message.append(QObject::tr("<br>%1 graph(s) that need manual corrections. You will be prompted to review them after you close this dialog.<br>").arg(assetsThatNeedManualInspection));
}
// Report
char resolvedBuffer[AZ_MAX_PATH_LEN] = { 0 };
AZStd::string outputFileName = AZStd::string::format("@devroot@/ScriptCanvasUpgradeReport.html");
AZ::IO::FileIOBase::GetInstance()->ResolvePath(outputFileName.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN);
AZStd::string urlToReport = AZStd::string::format("For more information see the <a href=\"%s\">Upgrade Report</a>.", resolvedBuffer);
message.append(QObject::tr("<br>%1").arg(urlToReport.c_str()));
// Backup
if (upgradeTool->HasBackup())
{
AZStd::string outputFileName2 = AZStd::string::format("@devroot@/ScriptCanvas_BACKUP");
AZ::IO::FileIOBase::GetInstance()->ResolvePath(outputFileName2.c_str(), resolvedBuffer, AZ_MAX_PATH_LEN);
AZStd::string backupPath = AZStd::string::format("<br>Open the <a href=\"%s\">Backup Folder</a>.", resolvedBuffer);
message.append(QObject::tr("%1").arg(backupPath.c_str()));
}
// Done upgrading, show details
QMessageBox mb(QMessageBox::Information,
QObject::tr("Upgrade Complete"),
message,
QMessageBox::Ok, this);
mb.setTextFormat(Qt::RichText);
centerPoint = frameGeometry().center();
mb.adjustSize();
mb.move(centerPoint.x() - width() / 2, centerPoint.y() - height() / 2);
mb.exec();
// If there are graphs that need manual correction, show the helper
if (assetsThatNeedManualInspection > 0)
{
UpgradeHelper* upgradeHelper = new UpgradeHelper(this);
upgradeHelper->show();
}
}
});
}
}
MainWindow::~MainWindow()
{
m_workspace->Save();
ScriptCanvas::BatchOperationNotificationBus::Handler::BusDisconnect();
GraphCanvas::AssetEditorRequestBus::Handler::BusDisconnect();
UndoNotificationBus::Handler::BusDisconnect();
UIRequestBus::Handler::BusDisconnect();
ScriptCanvasEditor::GeneralRequestBus::Handler::BusDisconnect();
GraphCanvas::AssetEditorAutomationRequestBus::Handler::BusDisconnect();
ScriptCanvas::ScriptCanvasSettingsRequestBus::Handler::BusDisconnect();
Clear();
delete m_nodePalette;
delete m_unitTestDockWidget;
delete m_statisticsDialog;
delete m_presetEditor;
delete m_workspace;
delete m_sceneContextMenu;
delete m_connectionContextMenu;
}
void MainWindow::CreateMenus()
{
// File menu
connect(ui->action_New_Script, &QAction::triggered, this, &MainWindow::OnFileNew);
ui->action_New_Script->setShortcut(QKeySequence(QKeySequence::New));
connect(ui->action_Open, &QAction::triggered, this, &MainWindow::OnFileOpen);
ui->action_Open->setShortcut(QKeySequence(QKeySequence::Open));
connect(ui->action_UpgradeTool, &QAction::triggered, this, &MainWindow::RunUpgradeTool);
ui->action_UpgradeTool->setVisible(true);
// List of recent files.
{
QMenu* recentMenu = new QMenu("Open &Recent");
for (int i = 0; i < m_recentActions.size(); ++i)
{
QAction* action = new QAction(this);
action->setVisible(false);
m_recentActions[i] = AZStd::make_pair(action, QMetaObject::Connection());
recentMenu->addAction(action);
}
connect(recentMenu, &QMenu::aboutToShow, this, &MainWindow::UpdateRecentMenu);
recentMenu->addSeparator();
// Clear Recent Files.
{
QAction* action = new QAction("&Clear Recent Files", this);
QObject::connect(action,
&QAction::triggered,
[this](bool /*checked*/)
{
ClearRecentFile();
UpdateRecentMenu();
});
recentMenu->addAction(action);
}
ui->menuFile->insertMenu(ui->action_Save, recentMenu);
ui->menuFile->insertSeparator(ui->action_Save);
}
connect(ui->action_Save, &QAction::triggered, this, &MainWindow::OnFileSaveCaller);
ui->action_Save->setShortcut(QKeySequence(QKeySequence::Save));
connect(ui->action_Save_As, &QAction::triggered, this, &MainWindow::OnFileSaveAsCaller);
ui->action_Save_As->setShortcut(QKeySequence(tr("Ctrl+Shift+S", "File|Save As...")));
QObject::connect(ui->action_Close,
&QAction::triggered,
[this](bool /*checked*/)
{
m_tabBar->tabCloseRequested(m_tabBar->currentIndex());
});
ui->action_Close->setShortcut(QKeySequence(QKeySequence::Close));
// Edit Menu
SetupEditMenu();
// View menu
connect(ui->action_ViewNodePalette, &QAction::triggered, this, &MainWindow::OnViewNodePalette);
// Disabling the Minimap since it does not play nicely with the Qt caching solution
// And causing some weird visual issues.
connect(ui->action_ViewMiniMap, &QAction::triggered, this, &MainWindow::OnViewMiniMap);
ui->action_ViewMiniMap->setVisible(false);
connect(ui->action_ViewProperties, &QAction::triggered, this, &MainWindow::OnViewProperties);
connect(ui->action_ViewBookmarks, &QAction::triggered, this, &MainWindow::OnBookmarks);
connect(ui->action_ViewVariableManager, &QAction::triggered, this, &MainWindow::OnVariableManager);
connect(m_variableDockWidget, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
connect(ui->action_ViewLogWindow, &QAction::triggered, this, &MainWindow::OnViewLogWindow);
connect(m_loggingWindow, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
connect(ui->action_ViewDebugger, &QAction::triggered, this, &MainWindow::OnViewDebugger);
connect(ui->action_ViewCommandLine, &QAction::triggered, this, &MainWindow::OnViewCommandLine);
connect(ui->action_ViewLog, &QAction::triggered, this, &MainWindow::OnViewLog);
connect(ui->action_GraphValidation, &QAction::triggered, this, &MainWindow::OnViewGraphValidation);
connect(ui->action_Debugging, &QAction::triggered, this, &MainWindow::OnViewDebuggingWindow);
connect(ui->action_ViewUnitTestManager, &QAction::triggered, this, &MainWindow::OnViewUnitTestManager);
connect(ui->action_NodeStatistics, &QAction::triggered, this, &MainWindow::OnViewStatisticsPanel);
connect(ui->action_PresetsEditor, &QAction::triggered, this, &MainWindow::OnViewPresetsEditor);
connect(ui->action_ViewRestoreDefaultLayout, &QAction::triggered, this, &MainWindow::OnRestoreDefaultLayout);
}
void MainWindow::SignalActiveSceneChanged(AZ::Data::AssetId assetId)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetId);
AZ::EntityId graphId;
if (memoryAsset)
{
graphId = memoryAsset->GetGraphId();
}
m_autoSaveTimer.stop();
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::PreOnActiveGraphChanged);
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::OnActiveGraphChanged, graphId);
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::PostOnActiveGraphChanged);
// The paste action refreshes based on the scene's mimetype
RefreshPasteAction();
bool enabled = false;
if (graphId.IsValid())
{
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphId, &GraphCanvas::SceneRequests::GetViewId);
AZ_Assert(viewId.IsValid(), "SceneRequest must return a valid ViewId");
if (viewId.IsValid())
{
GraphCanvas::ViewNotificationBus::Handler::BusDisconnect();
GraphCanvas::ViewNotificationBus::Handler::BusConnect(viewId);
enabled = memoryAsset->GetScriptCanvasId().IsValid();
}
}
UpdateMenuState(enabled);
}
void MainWindow::UpdateRecentMenu()
{
QStringList recentFiles = ReadRecentFiles();
int recentCount = 0;
for (auto filename : recentFiles)
{
if (!QFile::exists(filename))
{
continue;
}
auto& recent = m_recentActions[recentCount++];
recent.first->setText(QString("&%1 %2").arg(QString::number(recentCount), filename));
recent.first->setData(filename);
recent.first->setVisible(true);
QObject::disconnect(recent.second);
recent.second = QObject::connect(recent.first,
&QAction::triggered,
[this, filename](bool /*checked*/)
{
OpenFile(filename.toUtf8().data());
});
}
for (int i = recentCount; i < m_recentActions.size(); ++i)
{
auto& recent = m_recentActions[recentCount++];
recent.first->setVisible(false);
}
}
void MainWindow::OnViewVisibilityChanged(bool)
{
UpdateViewMenu();
}
void MainWindow::closeEvent(QCloseEvent* event)
{
// If we are in the middle of saving a graph. We don't want to close ourselves down and potentially retrigger the saving logic.
if (m_queueCloseRequest)
{
m_hasQueuedClose = true;
event->ignore();
return;
}
AssetTrackerRequests::AssetList unsavedAssets;
AssetTrackerRequestBus::BroadcastResult(unsavedAssets, &AssetTrackerRequests::GetUnsavedAssets);
for (int tabCounter = 0; tabCounter < m_tabBar->count(); ++tabCounter)
{
AZ::Data::AssetId assetId = m_tabBar->FindAssetId(tabCounter);
auto resultIterator = m_processedClosedAssetIds.insert(assetId);
if (!resultIterator.second)
{
continue;
}
const Tracker::ScriptCanvasFileState& fileState = GetAssetFileState(assetId);
if (fileState == Tracker::ScriptCanvasFileState::UNMODIFIED)
{
continue;
}
// Query the user.
SetActiveAsset(assetId);
QString tabName = m_tabBar->tabText(tabCounter);
UnsavedChangesOptions shouldSaveResults = ShowSaveDialog(tabName);
if (shouldSaveResults == UnsavedChangesOptions::SAVE)
{
Callbacks::OnSave saveCB = [this, assetId](bool isSuccessful, AZ::Data::AssetPtr, AZ::Data::AssetId)
{
if (isSuccessful)
{
// Continue closing.
qobject_cast<QWidget*>(parent())->close();
}
else
{
// Abort closing.
QMessageBox::critical(this, QString(), QObject::tr("Failed to save."));
m_processedClosedAssetIds.clear();
}
};
ActivateAndSaveAsset(assetId, saveCB);
event->ignore();
return;
}
else if (shouldSaveResults == UnsavedChangesOptions::CANCEL_WITHOUT_SAVING)
{
m_processedClosedAssetIds.clear();
event->ignore();
return;
}
else if (shouldSaveResults == UnsavedChangesOptions::CONTINUE_WITHOUT_SAVING &&
(fileState == Tracker::ScriptCanvasFileState::NEW || fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED))
{
CloseScriptCanvasAsset(assetId);
--tabCounter;
}
}
m_workspace->Save();
// Close all files.
AssetTrackerRequests::AssetList allAssets;
AssetTrackerRequestBus::BroadcastResult(allAssets, &AssetTrackerRequests::GetAssets);
for (auto trackedAsset : allAssets)
{
const AZ::Data::AssetId& assetId = trackedAsset->GetAsset().GetId();
CloseScriptCanvasAsset(assetId);
}
m_processedClosedAssetIds.clear();
event->accept();
}
UnsavedChangesOptions MainWindow::ShowSaveDialog(const QString& filename)
{
bool wasActive = m_autoSaveTimer.isActive();
if (wasActive)
{
m_autoSaveTimer.stop();
}
UnsavedChangesOptions shouldSaveResults = UnsavedChangesOptions::INVALID;
UnsavedChangesDialog dialog(filename, this);
dialog.exec();
shouldSaveResults = dialog.GetResult();
// If the auto save timer was active, and we cancelled our save dialog, we want
// to resume the auto save timer.
if (shouldSaveResults == UnsavedChangesOptions::CANCEL_WITHOUT_SAVING
|| shouldSaveResults == UnsavedChangesOptions::INVALID)
{
RestartAutoTimerSave(wasActive);
}
return shouldSaveResults;
}
void MainWindow::TriggerUndo()
{
GeneralEditorNotificationBus::Event(GetActiveScriptCanvasId(), &GeneralEditorNotifications::OnUndoRedoBegin);
DequeuePropertyGridUpdate();
UndoRequestBus::Event(GetActiveScriptCanvasId(), &UndoRequests::Undo);
SignalSceneDirty(m_activeAssetId);
m_propertyGrid->ClearSelection();
GeneralEditorNotificationBus::Event(GetActiveScriptCanvasId(), &GeneralEditorNotifications::OnUndoRedoEnd);
}
void MainWindow::TriggerRedo()
{
GeneralEditorNotificationBus::Event(GetActiveScriptCanvasId(), &GeneralEditorNotifications::OnUndoRedoBegin);
DequeuePropertyGridUpdate();
UndoRequestBus::Event(GetActiveScriptCanvasId(), &UndoRequests::Redo);
SignalSceneDirty(m_activeAssetId);
m_propertyGrid->ClearSelection();
GeneralEditorNotificationBus::Event(GetActiveScriptCanvasId(), &GeneralEditorNotifications::OnUndoRedoEnd);
}
void MainWindow::RegisterVariableType(const ScriptCanvas::Data::Type& variableType)
{
m_variablePaletteTypes.insert(ScriptCanvas::Data::ToAZType(variableType));
}
bool MainWindow::IsValidVariableType(const ScriptCanvas::Data::Type& dataType) const
{
return m_variableDockWidget->IsValidVariableType(dataType);
}
bool MainWindow::ShowSlotTypeSelector(ScriptCanvas::Slot* slot, const QPoint& scenePosition, VariablePaletteRequests::SlotSetup& outSetup)
{
AZ_Assert(slot, "A valid slot must be provided");
if (slot)
{
m_slotTypeSelector = new SlotTypeSelectorWidget(GetActiveScriptCanvasId(), this); // Recreate the widget every time because of https://bugreports.qt.io/browse/QTBUG-76509
m_slotTypeSelector->PopulateVariablePalette(m_variablePaletteTypes);
// Only set the slot name if the user has already configured this slot, so if they are creating
// for the first time they will see the placeholder text instead
bool isValidVariableType = false;
VariablePaletteRequestBus::BroadcastResult(isValidVariableType, &VariablePaletteRequests::IsValidVariableType, slot->GetDataType());
if (isValidVariableType)
{
m_slotTypeSelector->SetSlotName(slot->GetName());
}
m_slotTypeSelector->move(scenePosition);
m_slotTypeSelector->setEnabled(true);
m_slotTypeSelector->update();
if (m_slotTypeSelector->exec() != QDialog::Rejected)
{
outSetup.m_name = m_slotTypeSelector->GetSlotName();
outSetup.m_type = m_slotTypeSelector->GetSelectedType();
}
else
{
delete m_slotTypeSelector;
return false;
}
delete m_slotTypeSelector;
}
return true;
}
void MainWindow::OpenValidationPanel()
{
if (!m_validationDockWidget->isVisible())
{
OnViewGraphValidation();
}
}
void MainWindow::PostUndoPoint(ScriptCanvas::ScriptCanvasId scriptCanvasId)
{
bool isIdle = true;
UndoRequestBus::EventResult(isIdle, scriptCanvasId, &UndoRequests::IsIdle);
if (m_preventUndoStateUpdateCount == 0 && isIdle)
{
ScopedUndoBatch scopedUndoBatch("Modify Graph Canvas Scene");
UndoRequestBus::Event(scriptCanvasId, &UndoRequests::AddGraphItemChangeUndo, "Graph Change");
MarkAssetModified(m_activeAssetId);
}
const bool forceTimer = true;
RestartAutoTimerSave(forceTimer);
}
void MainWindow::SignalSceneDirty(AZ::Data::AssetId assetId)
{
MarkAssetModified(assetId);
}
void MainWindow::PushPreventUndoStateUpdate()
{
++m_preventUndoStateUpdateCount;
}
void MainWindow::PopPreventUndoStateUpdate()
{
if (m_preventUndoStateUpdateCount > 0)
{
--m_preventUndoStateUpdateCount;
}
}
void MainWindow::ClearPreventUndoStateUpdate()
{
m_preventUndoStateUpdateCount = 0;
}
void MainWindow::MarkAssetModified(const AZ::Data::AssetId& assetId)
{
if (!assetId.IsValid())
{
return;
}
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetId);
if (memoryAsset)
{
const auto& memoryAssetId = memoryAsset->GetId();
const Tracker::ScriptCanvasFileState& fileState = GetAssetFileState(memoryAssetId);
if (fileState != Tracker::ScriptCanvasFileState::NEW)
{
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::UpdateFileState, memoryAssetId, Tracker::ScriptCanvasFileState::MODIFIED);
}
}
}
void MainWindow::RefreshScriptCanvasAsset(const AZ::Data::Asset<ScriptCanvas::ScriptCanvasAssetBase>& asset)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, asset.GetId());
if (memoryAsset && asset.IsReady())
{
AZ::EntityId scGraphId = memoryAsset->GetScriptCanvasId();
GraphCanvas::SceneNotificationBus::MultiHandler::BusDisconnect(scGraphId);
AZ::EntityId graphCanvasId = GetGraphCanvasGraphId(scGraphId);
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::OnGraphRefreshed, graphCanvasId, graphCanvasId);
int tabIndex = -1;
if (IsTabOpen(asset.GetId(), tabIndex))
{
const AZStd::string& assetPath = memoryAsset->GetAbsolutePath();
m_tabBar->setTabToolTip(tabIndex, assetPath.c_str());
m_tabBar->SetTabText(tabIndex, memoryAsset->GetTabName().c_str(), memoryAsset->GetFileState());
}
if (graphCanvasId.IsValid())
{
GraphCanvas::SceneNotificationBus::MultiHandler::BusConnect(graphCanvasId);
GraphCanvas::SceneMimeDelegateRequestBus::Event(graphCanvasId, &GraphCanvas::SceneMimeDelegateRequests::AddDelegate, m_entityMimeDelegateId);
GraphCanvas::SceneRequestBus::Event(graphCanvasId, &GraphCanvas::SceneRequests::SetMimeType, Widget::NodePaletteDockWidget::GetMimeType());
GraphCanvas::SceneMemberNotificationBus::Event(graphCanvasId, &GraphCanvas::SceneMemberNotifications::OnSceneReady);
}
}
}
AZ::Outcome<int, AZStd::string> MainWindow::OpenScriptCanvasAssetId(const AZ::Data::AssetId& fileAssetId)
{
if (!fileAssetId.IsValid())
{
return AZ::Failure(AZStd::string("Unable to open asset with invalid asset id"));
}
int outTabIndex = m_tabBar->FindTab(fileAssetId);
if (outTabIndex >= 0)
{
m_tabBar->SelectTab(fileAssetId);
return AZ::Success(outTabIndex);
}
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, fileAssetId);
if (assetInfo.m_relativePath.empty())
{
return AZ::Failure(AZStd::string("Unknown AssetId"));
}
if (assetInfo.m_assetType != azrtti_typeid<ScriptCanvasAsset>() &&
assetInfo.m_assetType != azrtti_typeid<ScriptCanvasFunctionAsset>())
{
return AZ::Failure(AZStd::string("Invalid AssetId provided, it's not a Script Canvas supported type"));
}
AssetTrackerRequests::OnAssetReadyCallback onAssetReady = [this, fileAssetId, &outTabIndex](ScriptCanvasMemoryAsset& asset)
{
if (!asset.IsSourceInError())
{
outTabIndex = CreateAssetTab(asset.GetFileAssetId());
if (!m_isRestoringWorkspace)
{
SetActiveAsset(fileAssetId);
}
UpdateWorkspaceStatus(asset);
}
else
{
outTabIndex = -1;
m_loadingAssets.erase(fileAssetId);
}
};
m_loadingAssets.insert(fileAssetId);
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::Load, fileAssetId, assetInfo.m_assetType, onAssetReady);
if (outTabIndex >= 0)
{
return AZ::Success(outTabIndex);
}
else
{
return AZ::Failure(AZStd::string("Specified asset is in an error state and cannot be properly displayed."));
}
}
AZ::Outcome<int, AZStd::string> MainWindow::OpenScriptCanvasAsset(const ScriptCanvasMemoryAsset& scriptCanvasAsset, int tabIndex /*= -1*/)
{
const AZ::Data::AssetId& fileAssetId = scriptCanvasAsset.GetFileAssetId();
if (!fileAssetId.IsValid())
{
return AZ::Failure(AZStd::string("Unable to open asset with invalid asset id"));
}
if (scriptCanvasAsset.IsSourceInError())
{
if (!m_isRestoringWorkspace)
{
AZStd::string errorPath = scriptCanvasAsset.GetAbsolutePath();
if (errorPath.empty())
{
errorPath = m_errorFilePath;
}
if (m_queuedFocusOverride == fileAssetId)
{
m_queuedFocusOverride.SetInvalid();
}
QMessageBox::warning(this, "Unable to open source file", QString("Source File(%1) is in error and cannot be opened").arg(errorPath.c_str()), QMessageBox::StandardButton::Ok);
}
return AZ::Failure(AZStd::string("Source File is in error"));
}
int outTabIndex = m_tabBar->FindTab(fileAssetId);
if (outTabIndex >= 0)
{
if (!m_isRestoringWorkspace)
{
m_tabBar->SelectTab(fileAssetId);
}
return AZ::Success(outTabIndex);
}
outTabIndex = CreateAssetTab(fileAssetId, tabIndex);
if (outTabIndex == -1)
{
return AZ::Failure(AZStd::string::format("Unable to open existing Script Canvas Asset with id %s in the Script Canvas Editor", AssetHelpers::AssetIdToString(fileAssetId).c_str()));
}
AZStd::string assetPath = scriptCanvasAsset.GetAbsolutePath();
if (!assetPath.empty() && !m_loadingNewlySavedFile)
{
int eraseCount = m_loadingWorkspaceAssets.erase(fileAssetId);
if (eraseCount == 0)
{
AddRecentFile(assetPath.c_str());
}
}
if (!m_isRestoringWorkspace)
{
SetActiveAsset(fileAssetId);
}
GraphCanvas::GraphId graphCanvasGraphId = GetGraphCanvasGraphId(scriptCanvasAsset.GetScriptCanvasId());
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::OnGraphLoaded, graphCanvasGraphId);
GeneralAssetNotificationBus::Event(fileAssetId, &GeneralAssetNotifications::OnAssetVisualized);
AssetTrackerNotificationBus::MultiHandler::BusConnect(fileAssetId);
return AZ::Success(outTabIndex);
}
AZ::Outcome<int, AZStd::string> MainWindow::OpenScriptCanvasAsset(AZ::Data::AssetId scriptCanvasAssetId, int tabIndex /*= -1*/)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, scriptCanvasAssetId);
// If the asset is already tracked we can go directly to opening it.
if (memoryAsset)
{
return OpenScriptCanvasAsset(*memoryAsset, tabIndex);
}
else
{
return OpenScriptCanvasAssetId(scriptCanvasAssetId);
}
}
int MainWindow::CreateAssetTab(const AZ::Data::AssetId& assetId, int tabIndex)
{
return m_tabBar->InsertGraphTab(tabIndex, assetId);
}
AZ::Outcome<int, AZStd::string> MainWindow::UpdateScriptCanvasAsset(const AZ::Data::Asset<ScriptCanvas::ScriptCanvasAssetBase>& scriptCanvasAsset)
{
int outTabIndex = -1;
PushPreventUndoStateUpdate();
RefreshScriptCanvasAsset(scriptCanvasAsset);
if (IsTabOpen(scriptCanvasAsset.GetId(), outTabIndex))
{
RefreshActiveAsset();
}
PopPreventUndoStateUpdate();
if (outTabIndex == -1)
{
return AZ::Failure(AZStd::string::format("Script Canvas Asset %s is not open in a tab", scriptCanvasAsset.ToString<AZStd::string>().c_str()));
}
return AZ::Success(outTabIndex);
}
void MainWindow::RemoveScriptCanvasAsset(const AZ::Data::AssetId& assetId)
{
AssetHelpers::PrintInfo("RemoveScriptCanvasAsset : %s", AssetHelpers::AssetIdToString(assetId).c_str());
m_assetCreationRequests.erase(assetId);
GeneralAssetNotificationBus::Event(assetId, &GeneralAssetNotifications::OnAssetUnloaded);
AssetTrackerNotificationBus::MultiHandler::BusDisconnect(assetId);
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetId);
if (memoryAsset)
{
// Disconnect scene and asset editor buses
GraphCanvas::SceneNotificationBus::MultiHandler::BusDisconnect(memoryAsset->GetScriptCanvasId());
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::OnGraphUnloaded, memoryAsset->GetGraphId());
}
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::Close, assetId);
int tabIndex = m_tabBar->FindTab(assetId);
QVariant tabdata = m_tabBar->tabData(tabIndex);
if (tabdata.isValid())
{
auto tabAssetId = tabdata.value<AZ::Data::AssetId>();
SetActiveAsset(tabAssetId);
}
}
int MainWindow::CloseScriptCanvasAsset(const AZ::Data::AssetId& assetId)
{
int tabIndex = -1;
if (IsTabOpen(assetId, tabIndex))
{
OnTabCloseRequest(tabIndex);
}
return tabIndex;
}
bool MainWindow::CreateScriptCanvasAssetFor(const TypeDefs::EntityComponentId& requestingEntityId)
{
for (auto createdAssetPair : m_assetCreationRequests)
{
if (createdAssetPair.second == requestingEntityId)
{
return OpenScriptCanvasAssetId(createdAssetPair.first).IsSuccess();
}
}
AZ::Data::AssetId previousAssetId = m_activeAssetId;
OnFileNew();
bool createdNewAsset = m_activeAssetId != previousAssetId;
if (createdNewAsset)
{
m_assetCreationRequests[m_activeAssetId] = requestingEntityId;
}
if (m_isRestoringWorkspace)
{
m_queuedFocusOverride = m_activeAssetId;
}
return createdNewAsset;
}
bool MainWindow::IsScriptCanvasAssetOpen(const AZ::Data::AssetId& assetId) const
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetId);
return memoryAsset != nullptr;
}
const CategoryInformation* MainWindow::FindNodePaletteCategoryInformation(AZStd::string_view categoryPath) const
{
return m_nodePaletteModel.FindBestCategoryInformation(categoryPath);
}
const NodePaletteModelInformation* MainWindow::FindNodePaletteModelInformation(const ScriptCanvas::NodeTypeIdentifier& nodeType) const
{
return m_nodePaletteModel.FindNodePaletteInformation(nodeType);
}
void MainWindow::GetSuggestedFullFilenameToSaveAs(const AZ::Data::AssetId& assetId, AZStd::string& filePath, AZStd::string& fileFilter)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetId);
AZStd::string assetPath;
if (memoryAsset)
{
assetPath = memoryAsset->GetAbsolutePath();
AZ::Data::AssetType assetType = memoryAsset->GetAsset().GetType();
ScriptCanvasAssetHandler* assetHandler;
AssetTrackerRequestBus::BroadcastResult(assetHandler, &AssetTrackerRequests::GetAssetHandlerForType, assetType);
AZ_Assert(assetHandler, "Asset type must have a valid asset handler");
AZ::EBusAggregateResults<ScriptCanvas::AssetDescription*> results;
AssetRegistryRequestBus::BroadcastResult(results, &AssetRegistryRequests::GetAssetDescription, assetType);
ScriptCanvas::AssetDescription* description = nullptr;
for (auto item : results.values)
{
if (item->GetAssetType() == assetType)
{
description = item;
break;
}
}
AZ_Assert(description, "Asset type must have a valid description");
fileFilter = description->GetFileFilterImpl();
AZStd::string tabName;
AssetTrackerRequestBus::BroadcastResult(tabName, &AssetTrackerRequests::GetTabName, assetId);
assetPath = AZStd::string::format("%s/%s%s", description->GetSuggestedSavePathImpl(), tabName.c_str(), description->GetExtensionImpl());
}
AZStd::array<char, AZ::IO::MaxPathLength> resolvedPath;
AZ::IO::FileIOBase::GetInstance()->ResolvePath(assetPath.data(), resolvedPath.data(), resolvedPath.size());
filePath = resolvedPath.data();
}
void MainWindow::OpenFile(const char* fullPath)
{
m_errorFilePath = fullPath;
// Let's find the source file on disk
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
bool sourceInfoFound{};
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, fullPath, assetInfo, watchFolder);
if (sourceInfoFound)
{
const Tracker::ScriptCanvasFileState& fileState = GetAssetFileState(assetInfo.m_assetId);
if (fileState != Tracker::ScriptCanvasFileState::NEW && fileState != Tracker::ScriptCanvasFileState::INVALID)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetInfo.m_assetId);
if (m_tabBar->FindTab(assetInfo.m_assetId) < 0)
{
CreateAssetTab(assetInfo.m_assetId);
}
SetActiveAsset(memoryAsset->GetFileAssetId());
OpenNextFile();
return;
}
Callbacks::OnAssetReadyCallback onAssetReady = [this, fullPath, assetInfo](ScriptCanvasMemoryAsset&)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetInfo.m_assetId);
auto openOutcome = OpenScriptCanvasAsset(*memoryAsset);
if (openOutcome)
{
RunGraphValidation(false);
SetRecentAssetId(assetInfo.m_assetId);
}
else
{
AZ_Warning("Script Canvas", openOutcome, "%s", openOutcome.GetError().data());
}
OpenNextFile();
};
// TODO-LS the assetInfo.m_assetType is always null for some reason, I know in this case we want default assets so it's ok to hardcode it
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::Load, assetInfo.m_assetId, /*assetInfo.m_assetType*/azrtti_typeid<ScriptCanvasAsset>(), onAssetReady);
}
else
{
QMessageBox::warning(this, "Invalid Source Asset", QString("'%1' is not a valid asset path.").arg(fullPath), QMessageBox::Ok);
}
}
GraphCanvas::Endpoint MainWindow::HandleProposedConnection(const GraphCanvas::GraphId&, const GraphCanvas::ConnectionId&, const GraphCanvas::Endpoint& endpoint, const GraphCanvas::NodeId& nodeId, const QPoint& screenPoint)
{
GraphCanvas::Endpoint retVal;
GraphCanvas::ConnectionType connectionType = GraphCanvas::ConnectionType::CT_Invalid;
GraphCanvas::SlotRequestBus::EventResult(connectionType, endpoint.GetSlotId(), &GraphCanvas::SlotRequests::GetConnectionType);
GraphCanvas::NodeId currentTarget = nodeId;
while (!retVal.IsValid() && currentTarget.IsValid())
{
AZStd::vector<AZ::EntityId> targetSlotIds;
GraphCanvas::NodeRequestBus::EventResult(targetSlotIds, currentTarget, &GraphCanvas::NodeRequests::GetSlotIds);
AZStd::list< GraphCanvas::Endpoint > endpoints;
for (const auto& targetSlotId : targetSlotIds)
{
GraphCanvas::Endpoint proposedEndpoint(currentTarget, targetSlotId);
bool canCreate = false;
GraphCanvas::SlotRequestBus::EventResult(canCreate, endpoint.GetSlotId(), &GraphCanvas::SlotRequests::CanCreateConnectionTo, proposedEndpoint);
if (canCreate)
{
GraphCanvas::SlotGroup slotGroup = GraphCanvas::SlotGroups::Invalid;
GraphCanvas::SlotRequestBus::EventResult(slotGroup, targetSlotId, &GraphCanvas::SlotRequests::GetSlotGroup);
bool isVisible = slotGroup != GraphCanvas::SlotGroups::Invalid;
GraphCanvas::SlotLayoutRequestBus::EventResult(isVisible, currentTarget, &GraphCanvas::SlotLayoutRequests::IsSlotGroupVisible, slotGroup);
if (isVisible)
{
endpoints.push_back(proposedEndpoint);
}
}
}
if (!endpoints.empty())
{
if (endpoints.size() == 1)
{
retVal = endpoints.front();
}
else
{
QMenu menu;
for (GraphCanvas::Endpoint proposedEndpoint : endpoints)
{
QAction* action = aznew EndpointSelectionAction(proposedEndpoint);
menu.addAction(action);
}
QAction* result = menu.exec(screenPoint);
if (result != nullptr)
{
EndpointSelectionAction* selectedEnpointAction = static_cast<EndpointSelectionAction*>(result);
retVal = selectedEnpointAction->GetEndpoint();
}
else
{
retVal.Clear();
}
}
if (retVal.IsValid())
{
// Double safety check. This should be gauranteed by the previous checks. But just extra safety.
bool canCreateConnection = false;
GraphCanvas::SlotRequestBus::EventResult(canCreateConnection, endpoint.GetSlotId(), &GraphCanvas::SlotRequests::CanCreateConnectionTo, retVal);
if (!canCreateConnection)
{
retVal.Clear();
}
}
}
else
{
retVal.Clear();
}
if (!retVal.IsValid())
{
bool isWrapped = false;
GraphCanvas::NodeRequestBus::EventResult(isWrapped, currentTarget, &GraphCanvas::NodeRequests::IsWrapped);
if (isWrapped)
{
GraphCanvas::NodeRequestBus::EventResult(currentTarget, currentTarget, &GraphCanvas::NodeRequests::GetWrappingNode);
}
else
{
currentTarget.SetInvalid();
}
}
}
return retVal;
}
void MainWindow::OnFileNew()
{
MakeNewFile<ScriptCanvasAsset, ScriptCanvasAssetHandler>();
}
int MainWindow::InsertTabForAsset(AZStd::string_view assetPath, AZ::Data::AssetId assetId, int tabIndex)
{
int outTabIndex = -1;
{
// Insert tab block
AZStd::string tabName;
AzFramework::StringFunc::Path::GetFileName(assetPath.data(), tabName);
m_tabBar->InsertGraphTab(tabIndex, assetId);
if (!IsTabOpen(assetId, outTabIndex))
{
AZ_Assert(false, AZStd::string::format("Unable to open new Script Canvas Asset with id %s in the Script Canvas Editor", AssetHelpers::AssetIdToString(assetId).c_str()).c_str());
return -1;
}
m_tabBar->setTabToolTip(outTabIndex, assetPath.data());
}
return outTabIndex;
}
void MainWindow::UpdateUndoCache(AZ::Data::AssetId)
{
UndoCache* undoCache = nullptr;
UndoRequestBus::EventResult(undoCache, GetActiveScriptCanvasId(), &UndoRequests::GetSceneUndoCache);
if (undoCache)
{
undoCache->UpdateCache(GetActiveScriptCanvasId());
}
}
AZ::Outcome<int, AZStd::string> MainWindow::CreateScriptCanvasAsset(AZStd::string_view assetPath, AZ::Data::AssetType assetType, int tabIndex)
{
int outTabIndex = -1;
AZ::Data::AssetId newAssetId;
auto onAssetCreated = [this, assetPath, tabIndex, &outTabIndex](ScriptCanvasMemoryAsset& asset)
{
const AZ::Data::AssetId& assetId = asset.GetId();
outTabIndex = InsertTabForAsset(assetPath, assetId, tabIndex);
SetActiveAsset(assetId);
UpdateScriptCanvasAsset(asset.GetAsset());
AZ::EntityId scriptCanvasEntityId;
AssetTrackerRequestBus::BroadcastResult(scriptCanvasEntityId, &AssetTrackerRequests::GetScriptCanvasId, assetId);
GraphCanvas::GraphId graphCanvasGraphId = GetGraphCanvasGraphId(scriptCanvasEntityId);
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::OnGraphLoaded, graphCanvasGraphId);
};
AssetTrackerRequestBus::BroadcastResult(newAssetId, &AssetTrackerRequests::Create, assetPath, assetType, onAssetCreated);
return AZ::Success(outTabIndex);
}
bool MainWindow::OnFileSave(const Callbacks::OnSave& saveCB)
{
return SaveAssetImpl(m_activeAssetId, saveCB);
}
bool MainWindow::OnFileSaveAs(const Callbacks::OnSave& saveCB)
{
return SaveAssetAsImpl(m_activeAssetId, saveCB);
}
bool MainWindow::SaveAssetImpl(const AZ::Data::AssetId& assetId, const Callbacks::OnSave& saveCB)
{
if (!assetId.IsValid())
{
return false;
}
// TODO: Set graph read-only to prevent edits during save
bool saveSuccessful = false;
Tracker::ScriptCanvasFileState fileState = GetAssetFileState(assetId);
if (fileState == Tracker::ScriptCanvasFileState::NEW)
{
saveSuccessful = SaveAssetAsImpl(assetId, saveCB);
}
else if (fileState == Tracker::ScriptCanvasFileState::MODIFIED
|| fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED)
{
SaveAsset(assetId, saveCB);
saveSuccessful = true;
}
return saveSuccessful;
}
bool MainWindow::SaveAssetAsImpl(const AZ::Data::AssetId& inMemoryAssetId, const Callbacks::OnSave& saveCB)
{
if (!inMemoryAssetId.IsValid())
{
return false;
}
if (m_activeAssetId != inMemoryAssetId)
{
OnChangeActiveGraphTab(inMemoryAssetId);
}
PrepareAssetForSave(inMemoryAssetId);
AZStd::string suggestedFilename;
AZStd::string suggestedFileFilter;
GetSuggestedFullFilenameToSaveAs(inMemoryAssetId, suggestedFilename, suggestedFileFilter);
EnsureSaveDestinationDirectory(suggestedFilename);
QString filter = suggestedFileFilter.c_str();
QString selectedFile;
bool isValidFileName = false;
while (!isValidFileName)
{
selectedFile = QFileDialog::getSaveFileName(this, tr("Save As..."), suggestedFilename.data(), filter);
// If the selected file is empty that means we just cancelled.
// So we want to break out.
if (!selectedFile.isEmpty())
{
AZStd::string filePath = selectedFile.toUtf8().data();
AZStd::string fileName;
// Verify that the path is within the project
AZStd::string assetRoot;
AZStd::array<char, AZ::IO::MaxPathLength> assetRootChar;
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devroot@", assetRootChar.data(), assetRootChar.size());
assetRoot = assetRootChar.data();
/* if (!AZ::StringFunc::StartsWith(filePath, assetRoot))
{
QMessageBox::information(this, "Unable to Save", AZStd::string::format("You must select a path within the current project\n\n%s", assetRoot.c_str()).c_str());
}
else*/ if (AzFramework::StringFunc::Path::GetFileName(filePath.c_str(), fileName))
{
isValidFileName = !(fileName.empty());
}
else
{
QMessageBox::information(this, "Unable to Save", "File name cannot be empty");
}
}
else
{
break;
}
}
if (isValidFileName)
{
AZStd::string internalStringFile = selectedFile.toUtf8().data();
if (!AssetHelpers::IsValidSourceFile(internalStringFile, GetActiveScriptCanvasId()))
{
QMessageBox::warning(this, "Unable to Save", QString("File\n'%1'\n\nDoes not match the asset type of the current Graph.").arg(selectedFile));
return false;
}
SaveNewAsset(internalStringFile, inMemoryAssetId, saveCB);
m_newlySavedFile = internalStringFile;
// Forcing the file add here, since we are creating a new file
AddRecentFile(m_newlySavedFile.c_str());
return true;
}
return false;
}
void MainWindow::OnSaveCallback(bool saveSuccess, AZ::Data::AssetPtr fileAsset, AZ::Data::AssetId previousFileAssetId)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AZStd::string tabName = m_tabBar->tabText(m_tabBar->currentIndex()).toUtf8().data();
int saveTabIndex = m_tabBar->currentIndex();
if (saveSuccess)
{
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, fileAsset->GetId());
AZ_Assert(memoryAsset, "At this point we must have a MemoryAsset");
// Update the editor with the new information about this asset.
const AZ::Data::AssetId& fileAssetId = memoryAsset->GetFileAssetId();
saveTabIndex = m_tabBar->FindTab(fileAssetId);
// We've saved as over a new graph, so we need to close the old one.
if (saveTabIndex != m_tabBar->currentIndex())
{
// Invalidate the file asset id so we don't delete trigger the asset flow.
m_tabBar->setTabData(saveTabIndex, QVariant::fromValue(AZ::Data::AssetId()));
m_tabBar->CloseTab(saveTabIndex);
saveTabIndex = -1;
}
if (saveTabIndex < 0)
{
// This asset had not been saved yet, we will need to use the in memory asset Id to get the index.
saveTabIndex = m_tabBar->FindTab(memoryAsset->GetId());
if (saveTabIndex < 0)
{
// Finally, we may have Saved-As and we need the previous file asset Id to find the tab
saveTabIndex = m_tabBar->FindTab(previousFileAssetId);
}
}
AzFramework::StringFunc::Path::GetFileName(memoryAsset->GetAbsolutePath().c_str(), tabName);
// Update the tab's assetId to the file asset Id (necessary when saving a new asset)
m_tabBar->ConfigureTab(saveTabIndex, fileAssetId, tabName);
GeneralAssetNotificationBus::Event(memoryAsset->GetId(), &GeneralAssetNotifications::OnAssetVisualized);
auto requestorIter = m_assetCreationRequests.find(fileAsset->GetId());
if (requestorIter != m_assetCreationRequests.end())
{
auto editorComponents = AZ::EntityUtils::FindDerivedComponents<EditorScriptCanvasComponent>(requestorIter->second.first);
if (editorComponents.empty())
{
auto firstRequestBus = EditorScriptCanvasComponentRequestBus::FindFirstHandler(requestorIter->second.first);
if (firstRequestBus)
{
firstRequestBus->SetAssetId(fileAsset->GetId());
}
}
else
{
for (auto editorComponent : editorComponents)
{
if (editorComponent->GetId() == requestorIter->second.second)
{
editorComponent->SetAssetId(fileAsset->GetId());
break;
}
}
}
m_assetCreationRequests.erase(requestorIter);
}
// Soft switch the asset id here. We'll do a double scene switch down below to actually switch the active assetid
m_activeAssetId = fileAssetId;
}
else
{
// Use the previous memory asset to find what we had setup as our display
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, m_activeAssetId);
// Drop off our file modifier status for our display name when we fail to save.
if (tabName.at(tabName.size() -1) == '*')
{
tabName = tabName.substr(0, tabName.size() - 2);
}
}
if (m_tabBar->currentIndex() != saveTabIndex)
{
m_tabBar->setCurrentIndex(saveTabIndex);
}
else
{
// Something weird happens with our saving. Where we are relying on these scene changes being called.
AZ::Data::AssetId previousAssetId = m_activeAssetId;
OnChangeActiveGraphTab(AZ::Data::AssetId());
OnChangeActiveGraphTab(previousAssetId);
}
UpdateAssignToSelectionState();
OnSaveToast toast(tabName, GetActiveGraphCanvasGraphId(), saveSuccess);
const bool displayAsNotification = true;
RunGraphValidation(displayAsNotification);
// This is called during saving, so the is scaving flag is always true Need to update the state after this callback is complete. So schedule for next system tick.
AddSystemTickAction(SystemTickActionFlag::UpdateSaveMenuState);
if (m_closeCurrentGraphAfterSave)
{
AddSystemTickAction(SystemTickActionFlag::CloseCurrentGraph);
}
m_closeCurrentGraphAfterSave = false;
EnableAssetView(memoryAsset);
UnblockCloseRequests();
}
bool MainWindow::ActivateAndSaveAsset(const AZ::Data::AssetId& unsavedAssetId, const Callbacks::OnSave& saveCB)
{
SetActiveAsset(unsavedAssetId);
return OnFileSave(saveCB);
}
void MainWindow::SaveAsset(AZ::Data::AssetId assetId, const Callbacks::OnSave& onSave)
{
PrepareAssetForSave(assetId);
auto onSaveCallback = [this, onSave](bool saveSuccess, AZ::Data::AssetPtr asset, AZ::Data::AssetId previousAssetId)
{
OnSaveCallback(saveSuccess, asset, previousAssetId);
if (onSave)
{
AZStd::invoke(onSave, saveSuccess, asset, previousAssetId);
}
};
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::Save, assetId, onSaveCallback);
UpdateSaveState();
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, m_activeAssetId);
// Disable the current view if we are saving.
if (memoryAsset)
{
DisableAssetView(memoryAsset);
}
BlockCloseRequests();
}
void MainWindow::SaveNewAsset(AZStd::string_view path, AZ::Data::AssetId inMemoryAssetId, const Callbacks::OnSave& onSave)
{
PrepareAssetForSave(inMemoryAssetId);
auto onSaveCallback = [this, onSave](bool saveSuccess, AZ::Data::AssetPtr asset, AZ::Data::AssetId previousAssetId)
{
OnSaveCallback(saveSuccess, asset, previousAssetId);
if (onSave)
{
AZStd::invoke(onSave, saveSuccess, asset, previousAssetId);
}
};
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::SaveAs, inMemoryAssetId, path, onSaveCallback);
UpdateSaveState();
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, inMemoryAssetId);
// Disable the current view if we are saving.
if (memoryAsset)
{
DisableAssetView(memoryAsset);
}
BlockCloseRequests();
}
void MainWindow::OnFileOpen()
{
AZ::SerializeContext* serializeContext = nullptr;
EBUS_EVENT_RESULT(serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
AZ::Data::AssetId openId = ReadRecentAssetId();
AZStd::string assetRoot;
{
AZStd::array<char, AZ::IO::MaxPathLength> assetRootChar;
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@devassets@", assetRootChar.data(), assetRootChar.size());
assetRoot = assetRootChar.data();
}
AZStd::string assetPath;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetPath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, openId);
if (!assetPath.empty())
{
assetPath = AZStd::string::format("%s/%s", assetRoot.c_str(), assetPath.c_str());
}
if (!openId.IsValid() || !QFile::exists(assetPath.c_str()))
{
assetPath = AZStd::string::format("%s/scriptcanvas", assetRoot.c_str());
}
assetPath = AZStd::string::format("%s/scriptcanvas", assetRoot.c_str());
AZ::EBusAggregateResults<AZStd::vector<AZStd::string>> fileFilters;
AssetRegistryRequestBus::BroadcastResult(fileFilters, &AssetRegistryRequests::GetAssetHandlerFileFilters);
QString filter;
AZStd::set<AZStd::string> filterSet;
auto aggregateFilters = fileFilters.values;
for (auto aggregateFilters2 : fileFilters.values)
{
for (const AZStd::string& fileFilter : aggregateFilters2)
{
filterSet.insert(fileFilter);
}
}
QStringList nameFilters;
QString globalFilter;
for (auto fileFilter : filterSet)
{
nameFilters.push_back(fileFilter.c_str());
AZStd::size_t filterStart = fileFilter.find_last_of("(");
AZStd::size_t filterEnd = fileFilter.find_last_of(")");
if (filterStart != AZStd::string::npos
&& filterEnd != AZStd::string::npos)
{
AZStd::string substring = fileFilter.substr(filterStart + 1, (filterEnd - filterStart) - 1);
if (!globalFilter.isEmpty())
{
globalFilter.append(" ");
}
globalFilter.append(substring.c_str());
}
}
globalFilter = QString("All ScriptCanvas Files (%1)").arg(globalFilter);
nameFilters.push_front(globalFilter);
QFileDialog dialog(nullptr, tr("Open..."), assetPath.c_str());
dialog.setFileMode(QFileDialog::ExistingFiles);
dialog.setNameFilters(nameFilters);
if (dialog.exec() == QDialog::Accepted)
{
m_filesToOpen = dialog.selectedFiles();
OpenNextFile();
}
}
void MainWindow::SetupEditMenu()
{
ui->action_Undo->setShortcut(QKeySequence::Undo);
ui->action_Cut->setShortcut(QKeySequence(QKeySequence::Cut));
ui->action_Copy->setShortcut(QKeySequence(QKeySequence::Copy));
ui->action_Paste->setShortcut(QKeySequence(QKeySequence::Paste));
ui->action_Delete->setShortcut(QKeySequence(QKeySequence::Delete));
connect(ui->menuEdit, &QMenu::aboutToShow, this, &MainWindow::OnEditMenuShow);
// Edit Menu
connect(ui->action_Undo, &QAction::triggered, this, &MainWindow::TriggerUndo);
connect(ui->action_Redo, &QAction::triggered, this, &MainWindow::TriggerRedo);
connect(ui->action_Cut, &QAction::triggered, this, &MainWindow::OnEditCut);
connect(ui->action_Copy, &QAction::triggered, this, &MainWindow::OnEditCopy);
connect(ui->action_Paste, &QAction::triggered, this, &MainWindow::OnEditPaste);
connect(ui->action_Duplicate, &QAction::triggered, this, &MainWindow::OnEditDuplicate);
connect(ui->action_Delete, &QAction::triggered, this, &MainWindow::OnEditDelete);
connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &MainWindow::RefreshPasteAction);
connect(ui->action_RemoveUnusedNodes, &QAction::triggered, this, &MainWindow::OnRemoveUnusedNodes);
connect(ui->action_RemoveUnusedVariables, &QAction::triggered, this, &MainWindow::OnRemoveUnusedVariables);
connect(ui->action_RemoveUnusedElements, &QAction::triggered, this, &MainWindow::OnRemoveUnusedElements);
connect(ui->action_Screenshot, &QAction::triggered, this, &MainWindow::OnScreenshot);
connect(ui->action_SelectAll, &QAction::triggered, this, &MainWindow::OnSelectAll);
connect(ui->action_SelectInputs, &QAction::triggered, this, &MainWindow::OnSelectInputs);
connect(ui->action_SelectOutputs, &QAction::triggered, this, &MainWindow::OnSelectOutputs);
connect(ui->action_SelectConnected, &QAction::triggered, this, &MainWindow::OnSelectConnected);
connect(ui->action_ClearSelection, &QAction::triggered, this, &MainWindow::OnClearSelection);
connect(ui->action_EnableSelection, &QAction::triggered, this, &MainWindow::OnEnableSelection);
connect(ui->action_DisableSelection, &QAction::triggered, this, &MainWindow::OnDisableSelection);
connect(ui->action_AlignTop, &QAction::triggered, this, &MainWindow::OnAlignTop);
connect(ui->action_AlignBottom, &QAction::triggered, this, &MainWindow::OnAlignBottom);
connect(ui->action_AlignLeft, &QAction::triggered, this, &MainWindow::OnAlignLeft);
connect(ui->action_AlignRight, &QAction::triggered, this, &MainWindow::OnAlignRight);
ui->action_ZoomIn->setShortcuts({ QKeySequence(Qt::CTRL + Qt::Key_Plus),
QKeySequence(Qt::CTRL + Qt::Key_Equal)
});
// View Menu
connect(ui->action_ShowEntireGraph, &QAction::triggered, this, &MainWindow::OnShowEntireGraph);
connect(ui->action_ZoomIn, &QAction::triggered, this, &MainWindow::OnZoomIn);
connect(ui->action_ZoomOut, &QAction::triggered, this, &MainWindow::OnZoomOut);
connect(ui->action_ZoomSelection, &QAction::triggered, this, &MainWindow::OnZoomToSelection);
connect(ui->action_GotoStartOfChain, &QAction::triggered, this, &MainWindow::OnGotoStartOfChain);
connect(ui->action_GotoEndOfChain, &QAction::triggered, this, &MainWindow::OnGotoEndOfChain);
connect(ui->action_GlobalPreferences, &QAction::triggered, [this]()
{
ScriptCanvasEditor::SettingsDialog(ui->action_GlobalPreferences->text(), ScriptCanvas::ScriptCanvasId(), this).exec();
if (m_userSettings)
{
if (m_userSettings->m_autoSaveConfig.m_enabled)
{
m_allowAutoSave = true;
m_autoSaveTimer.setInterval(m_userSettings->m_autoSaveConfig.m_timeSeconds * 1000);
}
else
{
m_allowAutoSave = false;
}
}
});
connect(ui->action_GraphPreferences, &QAction::triggered, [this]() {
ScriptCanvas::ScriptCanvasId scriptCanvasId = GetActiveScriptCanvasId();
if (!scriptCanvasId.IsValid())
{
return;
}
m_autoSaveTimer.stop();
ScriptCanvasEditor::SettingsDialog(ui->action_GraphPreferences->text(), scriptCanvasId, this).exec();
});
}
void MainWindow::OnEditMenuShow()
{
RefreshGraphPreferencesAction();
ui->action_Screenshot->setEnabled(GetActiveGraphCanvasGraphId().IsValid());
ui->menuSelect->setEnabled(GetActiveGraphCanvasGraphId().IsValid());
ui->action_ClearSelection->setEnabled(GetActiveGraphCanvasGraphId().IsValid());
ui->menuAlign->setEnabled(GetActiveGraphCanvasGraphId().IsValid());
}
void MainWindow::RefreshPasteAction()
{
AZStd::string copyMimeType;
GraphCanvas::SceneRequestBus::EventResult(copyMimeType, GetActiveGraphCanvasGraphId(), &GraphCanvas::SceneRequests::GetCopyMimeType);
const bool pasteableClipboard = (!copyMimeType.empty() && QApplication::clipboard()->mimeData()->hasFormat(copyMimeType.c_str()))
|| GraphVariablesTableView::HasCopyVariableData();
ui->action_Paste->setEnabled(pasteableClipboard);
}
void MainWindow::RefreshGraphPreferencesAction()
{
ui->action_GraphPreferences->setEnabled(GetActiveGraphCanvasGraphId().IsValid());
}
void MainWindow::OnEditCut()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::CutSelection);
}
void MainWindow::OnEditCopy()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::CopySelection);
}
void MainWindow::OnEditPaste()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::Paste);
}
void MainWindow::OnEditDuplicate()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::DuplicateSelection);
}
void MainWindow::OnEditDelete()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::DeleteSelection);
}
void MainWindow::OnRemoveUnusedVariables()
{
AZ::EntityId scriptCanvasGraphId = GetActiveScriptCanvasId();
EditorGraphRequestBus::Event(scriptCanvasGraphId, &EditorGraphRequests::RemoveUnusedVariables);
}
void MainWindow::OnRemoveUnusedNodes()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::RemoveUnusedNodes);
}
void MainWindow::OnRemoveUnusedElements()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::RemoveUnusedElements);
}
void MainWindow::OnScreenshot()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::ScreenshotSelection);
}
void MainWindow::OnSelectAll()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::SelectAll);
}
void MainWindow::OnSelectInputs()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::SelectAllRelative, GraphCanvas::ConnectionType::CT_Input);
}
void MainWindow::OnSelectOutputs()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::SelectAllRelative, GraphCanvas::ConnectionType::CT_Output);
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
}
void MainWindow::OnSelectConnected()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::SelectConnectedNodes);
}
void MainWindow::OnClearSelection()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::ClearSelection);
}
void MainWindow::OnEnableSelection()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::EnableSelection);
}
void MainWindow::OnDisableSelection()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::DisableSelection);
}
void MainWindow::OnAlignTop()
{
GraphCanvas::AlignConfig alignConfig;
alignConfig.m_horAlign = GraphCanvas::GraphUtils::HorizontalAlignment::None;
alignConfig.m_verAlign = GraphCanvas::GraphUtils::VerticalAlignment::Top;
alignConfig.m_alignTime = GetAlignmentTime();
AlignSelected(alignConfig);
}
void MainWindow::OnAlignBottom()
{
GraphCanvas::AlignConfig alignConfig;
alignConfig.m_horAlign = GraphCanvas::GraphUtils::HorizontalAlignment::None;
alignConfig.m_verAlign = GraphCanvas::GraphUtils::VerticalAlignment::Bottom;
alignConfig.m_alignTime = GetAlignmentTime();
AlignSelected(alignConfig);
}
void MainWindow::OnAlignLeft()
{
GraphCanvas::AlignConfig alignConfig;
alignConfig.m_horAlign = GraphCanvas::GraphUtils::HorizontalAlignment::Left;
alignConfig.m_verAlign = GraphCanvas::GraphUtils::VerticalAlignment::None;
alignConfig.m_alignTime = GetAlignmentTime();
AlignSelected(alignConfig);
}
void MainWindow::OnAlignRight()
{
GraphCanvas::AlignConfig alignConfig;
alignConfig.m_horAlign = GraphCanvas::GraphUtils::HorizontalAlignment::Right;
alignConfig.m_verAlign = GraphCanvas::GraphUtils::VerticalAlignment::None;
alignConfig.m_alignTime = GetAlignmentTime();
AlignSelected(alignConfig);
}
void MainWindow::AlignSelected(const GraphCanvas::AlignConfig& alignConfig)
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
AZStd::vector< GraphCanvas::NodeId > selectedNodes;
GraphCanvas::SceneRequestBus::EventResult(selectedNodes, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetSelectedNodes);
GraphCanvas::GraphUtils::AlignNodes(selectedNodes, alignConfig);
}
void MainWindow::OnShowEntireGraph()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::ShowEntireGraph);
}
void MainWindow::OnZoomIn()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::ZoomIn);
}
void MainWindow::OnZoomOut()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::ZoomOut);
}
void MainWindow::OnZoomToSelection()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::CenterOnSelection);
}
void MainWindow::OnGotoStartOfChain()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::CenterOnStartOfChain);
}
void MainWindow::OnGotoEndOfChain()
{
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::CenterOnEndOfChain);
}
void MainWindow::UpdateWorkspaceStatus(const ScriptCanvasMemoryAsset& memoryAsset)
{
AZ::Data::AssetId fileAssetId = memoryAsset.GetFileAssetId();
AZ::Data::AssetId memoryAssetId = memoryAsset.GetId();
int eraseCount = m_loadingAssets.erase(fileAssetId);
if (eraseCount > 0)
{
AZStd::string rootFilePath;
AZ::Data::AssetInfo assetInfo = AssetHelpers::GetAssetInfo(fileAssetId, rootFilePath);
// Don't want to use the join since I don't want the normalized path
if (!rootFilePath.empty() && !assetInfo.m_relativePath.empty())
{
eraseCount = m_loadingWorkspaceAssets.erase(fileAssetId);
if (eraseCount == 0)
{
AZStd::string fullPath = AZStd::string::format("%s/%s", rootFilePath.c_str(), assetInfo.m_relativePath.c_str());
AddRecentFile(fullPath.c_str());
}
}
}
}
void MainWindow::OnCanUndoChanged(bool canUndo)
{
ui->action_Undo->setEnabled(canUndo);
}
void MainWindow::OnCanRedoChanged(bool canRedo)
{
ui->action_Redo->setEnabled(canRedo);
}
bool MainWindow::CanShowNetworkSettings()
{
return m_userSettings->m_experimentalSettings.GetShowNetworkProperties();
}
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::HandleContextMenu(GraphCanvas::EditorContextMenu& editorContextMenu, const AZ::EntityId& memberId, const QPoint& screenPoint, const QPointF& scenePoint) const
{
AZ::Vector2 sceneVector(aznumeric_cast<float>(scenePoint.x()), aznumeric_cast<float>(scenePoint.y()));
GraphCanvas::GraphId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
editorContextMenu.RefreshActions(graphCanvasGraphId, memberId);
QAction* result = editorContextMenu.exec(screenPoint);
GraphCanvas::ContextMenuAction* contextMenuAction = qobject_cast<GraphCanvas::ContextMenuAction*>(result);
if (contextMenuAction)
{
return contextMenuAction->TriggerAction(graphCanvasGraphId, sceneVector);
}
else
{
return GraphCanvas::ContextMenuAction::SceneReaction::Nothing;
}
}
void MainWindow::OnAutoSave()
{
if (m_allowAutoSave)
{
const Tracker::ScriptCanvasFileState& fileState = GetAssetFileState(m_activeAssetId);
if (fileState != Tracker::ScriptCanvasFileState::INVALID && fileState != Tracker::ScriptCanvasFileState::NEW)
{
OnFileSaveCaller();
}
}
}
//! GeneralRequestBus
void MainWindow::OnChangeActiveGraphTab(AZ::Data::AssetId assetId)
{
SetActiveAsset(assetId);
}
AZ::EntityId MainWindow::GetActiveGraphCanvasGraphId() const
{
AZ::EntityId graphId;
AssetTrackerRequestBus::BroadcastResult(graphId, &AssetTrackerRequests::GetGraphId, m_activeAssetId);
return graphId;
}
ScriptCanvas::ScriptCanvasId MainWindow::GetActiveScriptCanvasId() const
{
ScriptCanvas::ScriptCanvasId sceneId;
AssetTrackerRequestBus::BroadcastResult(sceneId, &AssetTrackerRequests::GetScriptCanvasId, m_activeAssetId);
return sceneId;
}
GraphCanvas::GraphId MainWindow::GetGraphCanvasGraphId(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
AZ::EntityId graphCanvasId;
AssetTrackerRequestBus::BroadcastResult(graphCanvasId, &AssetTrackerRequests::GetGraphCanvasId, scriptCanvasId);
return graphCanvasId;
}
GraphCanvas::GraphId MainWindow::FindGraphCanvasGraphIdByAssetId(const AZ::Data::AssetId& assetId) const
{
AZ::EntityId graphId;
AssetTrackerRequestBus::BroadcastResult(graphId, &AssetTrackerRequests::GetGraphId, assetId);
return graphId;
}
ScriptCanvas::ScriptCanvasId MainWindow::FindScriptCanvasIdByAssetId(const AZ::Data::AssetId& assetId) const
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
AssetTrackerRequestBus::BroadcastResult(scriptCanvasId, &AssetTrackerRequests::GetScriptCanvasId, assetId);
return scriptCanvasId;
}
ScriptCanvas::ScriptCanvasId MainWindow::GetScriptCanvasId(const GraphCanvas::GraphId& graphCanvasGraphId) const
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
AssetTrackerRequestBus::BroadcastResult(scriptCanvasId, &AssetTrackerRequests::GetScriptCanvasIdFromGraphId, graphCanvasGraphId);
return scriptCanvasId;
}
bool MainWindow::IsInUndoRedo(const AZ::EntityId& graphCanvasGraphId) const
{
bool isActive = false;
UndoRequestBus::EventResult(isActive, GetScriptCanvasId(graphCanvasGraphId), &UndoRequests::IsActive);
return isActive;
}
bool MainWindow::IsScriptCanvasInUndoRedo(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
{
if (GetActiveScriptCanvasId() == scriptCanvasId)
{
bool isInUndoRedo = false;
UndoRequestBus::BroadcastResult(isInUndoRedo, &UndoRequests::IsActive);
return isInUndoRedo;
}
return false;
}
bool MainWindow::IsActiveGraphInUndoRedo() const
{
bool isActive = false;
UndoRequestBus::EventResult(isActive, GetActiveScriptCanvasId(), &UndoRequests::IsActive);
return isActive;
}
QVariant MainWindow::GetTabData(const AZ::Data::AssetId& assetId)
{
for (int tabIndex = 0; tabIndex < m_tabBar->count(); ++tabIndex)
{
QVariant tabdata = m_tabBar->tabData(tabIndex);
if (tabdata.isValid())
{
auto tabAssetId = tabdata.value<AZ::Data::AssetId>();
if (tabAssetId == assetId)
{
return tabdata;
}
}
}
return QVariant();
}
bool MainWindow::IsTabOpen(const AZ::Data::AssetId& fileAssetId, int& outTabIndex) const
{
int tabIndex = m_tabBar->FindTab(fileAssetId);
if (-1 != tabIndex)
{
outTabIndex = tabIndex;
return true;
}
return false;
}
void MainWindow::ReconnectSceneBuses(AZ::Data::AssetId previousAssetId, AZ::Data::AssetId nextAssetId)
{
ScriptCanvasMemoryAsset::pointer previousAsset;
AssetTrackerRequestBus::BroadcastResult(previousAsset, &AssetTrackerRequests::GetAsset, previousAssetId);
ScriptCanvasMemoryAsset::pointer nextAsset;
AssetTrackerRequestBus::BroadcastResult(nextAsset, &AssetTrackerRequests::GetAsset, nextAssetId);
// Disconnect previous asset
AZ::EntityId previousScriptCanvasSceneId;
if (previousAsset)
{
previousScriptCanvasSceneId = previousAsset->GetScriptCanvasId();
GraphCanvas::SceneNotificationBus::MultiHandler::BusDisconnect(previousScriptCanvasSceneId);
}
AZ::EntityId nextAssetGraphCanvasId;
if (nextAsset)
{
// Connect the next asset
nextAssetGraphCanvasId = nextAsset->GetGraphId();
if (nextAssetGraphCanvasId.IsValid())
{
GraphCanvas::SceneNotificationBus::MultiHandler::BusConnect(nextAssetGraphCanvasId);
GraphCanvas::SceneMimeDelegateRequestBus::Event(nextAssetGraphCanvasId, &GraphCanvas::SceneMimeDelegateRequests::AddDelegate, m_entityMimeDelegateId);
GraphCanvas::SceneRequestBus::Event(nextAssetGraphCanvasId, &GraphCanvas::SceneRequests::SetMimeType, Widget::NodePaletteDockWidget::GetMimeType());
GraphCanvas::SceneMemberNotificationBus::Event(nextAssetGraphCanvasId, &GraphCanvas::SceneMemberNotifications::OnSceneReady);
}
}
// Notify about the graph refresh
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::OnGraphRefreshed, previousScriptCanvasSceneId, nextAssetGraphCanvasId);
}
void MainWindow::SetActiveAsset(const AZ::Data::AssetId& fileAssetId)
{
if (m_activeAssetId == fileAssetId)
{
return;
}
AssetHelpers::PrintInfo("SetActiveAsset : from: %s to %s", AssetHelpers::AssetIdToString(m_activeAssetId).c_str(), AssetHelpers::AssetIdToString(fileAssetId).c_str());
if (fileAssetId.IsValid())
{
if (m_tabBar->FindTab(fileAssetId) >= 0)
{
QSignalBlocker signalBlocker(m_tabBar);
m_tabBar->SelectTab(fileAssetId);
}
else
{
AZ_Assert(false, "A graph was opened, but a tab was not created for it.");
}
}
if (m_activeAssetId.IsValid())
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, m_activeAssetId);
// If we are saving the asset, the Id may have changed from the in-memory to the file asset Id, in that case,
// there's no need to hide the view or remove the widget
if (memoryAsset && memoryAsset->GetView())
{
memoryAsset->GetView()->hide();
m_layout->removeWidget(memoryAsset->GetView());
}
}
if (fileAssetId.IsValid())
{
AZ::Data::AssetId previousAssetId = m_activeAssetId;
m_activeAssetId = fileAssetId;
RefreshActiveAsset();
ReconnectSceneBuses(previousAssetId, m_activeAssetId);
}
else
{
AZ::Data::AssetId previousAssetId = m_activeAssetId;
m_activeAssetId.SetInvalid();
m_emptyCanvas->show();
ReconnectSceneBuses(previousAssetId, m_activeAssetId);
SignalActiveSceneChanged(AZ::Data::AssetId());
}
UpdateUndoCache(fileAssetId);
RefreshSelection();
}
void MainWindow::RefreshActiveAsset()
{
if (m_activeAssetId.IsValid())
{
AssetHelpers::PrintInfo("RefreshActiveAsset : m_activeAssetId (%s)", AssetHelpers::AssetIdToString(m_activeAssetId).c_str());
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, m_activeAssetId);
if (memoryAsset)
{
AZ::EntityId sceneEntityId = memoryAsset->GetScriptCanvasId();
const auto& scriptCanvasAsset = memoryAsset->GetAsset();
if (scriptCanvasAsset.IsReady() && scriptCanvasAsset.Get()->GetScriptCanvasEntity()->GetState() == AZ::Entity::State::Active)
{
if (!memoryAsset->GetView())
{
memoryAsset->CreateView(m_tabBar);
}
auto view = memoryAsset->GetView();
AZ_Assert(view, "Asset should have a view");
if (view)
{
AssetHelpers::PrintInfo("RefreshActiveAsset : m_activeAssetId (%s)", AssetHelpers::AssetIdToString(m_activeAssetId).c_str());
view->ShowScene(sceneEntityId);
m_layout->addWidget(view);
view->show();
m_emptyCanvas->hide();
}
SignalActiveSceneChanged(m_activeAssetId);
}
}
else
{
// If we couldn't load a memory asset for our active asset. Just set ourselves to invalid.
SetActiveAsset({});
}
}
}
void MainWindow::Clear()
{
m_tabBar->CloseAllTabs();
AssetTrackerRequests::AssetList assets;
AssetTrackerRequestBus::BroadcastResult(assets, &AssetTrackerRequests::GetAssets);
for (auto asset : assets)
{
RemoveScriptCanvasAsset(asset->GetAsset().GetId());
}
SetActiveAsset({});
}
void MainWindow::OnTabCloseButtonPressed(int index)
{
QVariant tabdata = m_tabBar->tabData(index);
if (tabdata.isValid())
{
auto fileAssetId = tabdata.value<AZ::Data::AssetId>();
Tracker::ScriptCanvasFileState fileState;
AssetTrackerRequestBus::BroadcastResult(fileState, &AssetTrackerRequests::GetFileState, fileAssetId);
bool isSaving = false;
AssetTrackerRequestBus::BroadcastResult(isSaving, &AssetTrackerRequests::IsSaving, fileAssetId);
if (isSaving)
{
m_closeCurrentGraphAfterSave = true;
return;
}
UnsavedChangesOptions saveDialogResults = UnsavedChangesOptions::CONTINUE_WITHOUT_SAVING;
if (!isSaving && (fileState == Tracker::ScriptCanvasFileState::NEW || fileState == Tracker::ScriptCanvasFileState::MODIFIED || fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED))
{
SetActiveAsset(fileAssetId);
AZStd::string tabName;
AssetTrackerRequestBus::BroadcastResult(tabName, &AssetTrackerRequests::GetTabName, fileAssetId);
saveDialogResults = ShowSaveDialog(tabName.c_str());
}
if (saveDialogResults == UnsavedChangesOptions::SAVE)
{
auto saveCB = [this](bool isSuccessful, AZ::Data::AssetPtr asset, AZ::Data::AssetId)
{
if (isSuccessful)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, asset->GetId());
AZ_Assert(memoryAsset, "At this point we must have a MemoryAsset");
int tabIndex = -1;
if (IsTabOpen(memoryAsset->GetFileAssetId(), tabIndex))
{
OnTabCloseRequest(tabIndex);
}
}
else
{
QMessageBox::critical(this, QString(), QObject::tr("Failed to save."));
}
};
if (fileState == Tracker::ScriptCanvasFileState::NEW)
{
SaveAssetAsImpl(fileAssetId, saveCB);
}
else
{
SaveAsset(fileAssetId, saveCB);
}
}
else if (saveDialogResults == UnsavedChangesOptions::CONTINUE_WITHOUT_SAVING)
{
OnTabCloseRequest(index);
}
}
}
void MainWindow::SaveTab(int index)
{
QVariant tabdata = m_tabBar->tabData(index);
if (tabdata.isValid())
{
auto assetId = tabdata.value<AZ::Data::AssetId>();
SaveAssetImpl(assetId, nullptr);
}
}
void MainWindow::CloseAllTabs()
{
m_isClosingTabs = true;
m_skipTabOnClose.SetInvalid();
CloseNextTab();
}
void MainWindow::CloseAllTabsBut(int index)
{
QVariant tabdata = m_tabBar->tabData(index);
if (tabdata.isValid())
{
auto assetId = tabdata.value<AZ::Data::AssetId>();
m_isClosingTabs = true;
m_skipTabOnClose = assetId;
CloseNextTab();
}
}
void MainWindow::CopyPathToClipboard(int index)
{
QVariant tabdata = m_tabBar->tabData(index);
if (tabdata.isValid())
{
QClipboard* clipBoard = QGuiApplication::clipboard();
auto assetId = tabdata.value<AZ::Data::AssetId>();
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetId);
if (memoryAsset)
{
clipBoard->setText(memoryAsset->GetAbsolutePath().c_str());
}
else
{
clipBoard->setText(m_tabBar->tabText(index));
}
}
}
void MainWindow::OnActiveFileStateChanged()
{
UpdateSaveState();
UpdateAssignToSelectionState();
}
void MainWindow::CloseNextTab()
{
if (m_isClosingTabs)
{
if (m_tabBar->count() == 0
|| (m_tabBar->count() == 1 && m_skipTabOnClose.IsValid()))
{
m_isClosingTabs = false;
m_skipTabOnClose.SetInvalid();
return;
}
int tab = 0;
while (tab < m_tabBar->count())
{
QVariant tabdata = m_tabBar->tabData(tab);
if (tabdata.isValid())
{
auto assetId = tabdata.value<AZ::Data::AssetId>();
if (assetId != m_skipTabOnClose)
{
break;
}
}
tab++;
}
m_tabBar->tabCloseRequested(tab);
}
}
void MainWindow::OnTabCloseRequest(int index)
{
QVariant tabdata = m_tabBar->tabData(index);
if (tabdata.isValid())
{
auto tabAssetId = tabdata.value<AZ::Data::AssetId>();
if (tabAssetId == m_activeAssetId)
{
SetActiveAsset({});
}
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, tabAssetId);
if (memoryAsset && memoryAsset->GetView())
{
memoryAsset->GetView()->hide();
}
m_tabBar->CloseTab(index);
m_tabBar->update();
RemoveScriptCanvasAsset(tabAssetId);
if (m_tabBar->count() == 0)
{
// The last tab has been removed.
SetActiveAsset({});
}
// Handling various close all events because the save is async need to deal with this in a bunch of different ways
// Always want to trigger this, even if we don't have any active tabs to avoid doubling the clean-up
// information
AddSystemTickAction(SystemTickActionFlag::CloseNextTabAction);
}
}
void MainWindow::OnNodeAdded(const AZ::EntityId& nodeId, bool isPaste)
{
// Handle special-case where if a method node is created that has an AZ::Event output slot,
// we will automatically create the AZ::Event Handler node for the user
GraphCanvas::GraphId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
AZStd::vector<GraphCanvas::SlotId> outputDataSlotIds;
GraphCanvas::NodeRequestBus::EventResult(outputDataSlotIds, nodeId, &GraphCanvas::NodeRequests::FindVisibleSlotIdsByType, GraphCanvas::CT_Output, GraphCanvas::SlotTypes::DataSlot);
for (const auto& slotId : outputDataSlotIds)
{
if (!IsInUndoRedo(graphCanvasGraphId) && !isPaste && CreateAzEventHandlerSlotMenuAction::FindBehaviorMethodWithAzEventReturn(graphCanvasGraphId, slotId))
{
CreateAzEventHandlerSlotMenuAction eventHandlerAction(this);
eventHandlerAction.RefreshAction(graphCanvasGraphId, slotId);
AZ::Vector2 position;
GraphCanvas::GeometryRequestBus::EventResult(position, nodeId, &GraphCanvas::GeometryRequests::GetPosition);
eventHandlerAction.TriggerAction(graphCanvasGraphId, position);
break;
}
}
}
void MainWindow::OnSelectionChanged()
{
QueuePropertyGridUpdate();
}
void MainWindow::OnVariableSelectionChanged(const AZStd::vector<AZ::EntityId>& variablePropertyIds)
{
m_selectedVariableIds = variablePropertyIds;
QueuePropertyGridUpdate();
}
void MainWindow::QueuePropertyGridUpdate()
{
// Selection will be ignored when a delete operation is are taking place to prevent slowdown from processing
// too many events at once.
if (!m_ignoreSelection && !m_isInAutomation)
{
AddSystemTickAction(SystemTickActionFlag::RefreshPropertyGrid);
}
}
void MainWindow::DequeuePropertyGridUpdate()
{
RemoveSystemTickAction(SystemTickActionFlag::RefreshPropertyGrid);
}
void MainWindow::SetDefaultLayout()
{
// Disable updates while we restore the layout to avoid temporary glitches
// as the panes are moved around
setUpdatesEnabled(false);
if (m_commandLine)
{
m_commandLine->hide();
}
if (m_validationDockWidget)
{
addDockWidget(Qt::BottomDockWidgetArea, m_validationDockWidget);
m_validationDockWidget->setFloating(false);
m_validationDockWidget->hide();
}
if (m_logPanel)
{
addDockWidget(Qt::BottomDockWidgetArea, m_logPanel);
m_logPanel->setFloating(false);
m_logPanel->hide();
}
/* Disable Mini-map until we fix rendering performance
if (m_minimap)
{
addDockWidget(Qt::LeftDockWidgetArea, m_minimap);
m_minimap->setFloating(false);
m_minimap->hide();
}
*/
if (m_nodePalette)
{
addDockWidget(Qt::LeftDockWidgetArea, m_nodePalette);
m_nodePalette->setFloating(false);
m_nodePalette->show();
}
if (m_variableDockWidget)
{
addDockWidget(Qt::RightDockWidgetArea, m_variableDockWidget);
m_variableDockWidget->setFloating(false);
m_variableDockWidget->show();
}
if (m_unitTestDockWidget)
{
addDockWidget(Qt::LeftDockWidgetArea, m_unitTestDockWidget);
m_unitTestDockWidget->setFloating(false);
m_unitTestDockWidget->hide();
}
if (m_loggingWindow)
{
addDockWidget(Qt::BottomDockWidgetArea, m_loggingWindow);
m_loggingWindow->setFloating(false);
m_loggingWindow->hide();
}
if (m_propertyGrid)
{
addDockWidget(Qt::RightDockWidgetArea, m_propertyGrid);
m_propertyGrid->setFloating(false);
m_propertyGrid->show();
}
if (m_bookmarkDockWidget)
{
addDockWidget(Qt::RightDockWidgetArea, m_bookmarkDockWidget);
m_bookmarkDockWidget->setFloating(false);
m_bookmarkDockWidget->hide();
}
/* Disable mini-map until we fix rendering performance
if (m_minimap)
{
addDockWidget(Qt::RightDockWidgetArea, m_minimap);
m_minimap->setFloating(false);
m_minimap->hide();
}
*/
resizeDocks(
{ m_nodePalette, m_propertyGrid },
{ static_cast<int>(size().width() * 0.15f), static_cast<int>(size().width() * 0.2f) },
Qt::Horizontal);
resizeDocks({ m_nodePalette, m_minimap },
{ static_cast<int>(size().height() * 0.70f), static_cast<int>(size().height() * 0.30f) },
Qt::Vertical);
resizeDocks({ m_propertyGrid, m_variableDockWidget },
{ static_cast<int>(size().height() * 0.70f), static_cast<int>(size().height() * 0.30f) },
Qt::Vertical);
resizeDocks({ m_validationDockWidget }, { static_cast<int>(size().height() * 0.01) }, Qt::Vertical);
// Disabled until debugger is implemented
//resizeDocks({ m_logPanel }, { static_cast<int>(size().height() * 0.1f) }, Qt::Vertical);
// Re-enable updates now that we've finished adjusting the layout
setUpdatesEnabled(true);
m_defaultLayout = saveState();
UpdateViewMenu();
}
void MainWindow::RefreshSelection()
{
ScriptCanvas::ScriptCanvasId scriptCanvasId = GetActiveScriptCanvasId();
AZ::EntityId graphCanvasGraphId;
EditorGraphRequestBus::EventResult(graphCanvasGraphId, scriptCanvasId, &EditorGraphRequests::GetGraphCanvasGraphId);
bool hasCopiableSelection = false;
bool hasSelection = false;
if (m_activeAssetId.IsValid())
{
if (graphCanvasGraphId.IsValid())
{
// Get the selected nodes.
GraphCanvas::SceneRequestBus::EventResult(hasCopiableSelection, graphCanvasGraphId, &GraphCanvas::SceneRequests::HasCopiableSelection);
}
AZStd::vector< AZ::EntityId > selection;
GraphCanvas::SceneRequestBus::EventResult(selection, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetSelectedItems);
selection.reserve(selection.size() + m_selectedVariableIds.size());
selection.insert(selection.end(), m_selectedVariableIds.begin(), m_selectedVariableIds.end());
if (!selection.empty())
{
hasSelection = true;
m_propertyGrid->SetSelection(selection);
}
else
{
m_propertyGrid->ClearSelection();
}
}
else
{
m_propertyGrid->ClearSelection();
}
// cut, copy and duplicate only works for specified items
ui->action_Cut->setEnabled(hasCopiableSelection);
ui->action_Copy->setEnabled(hasCopiableSelection);
ui->action_Duplicate->setEnabled(hasCopiableSelection);
// Delete will work for anything that is selectable
ui->action_Delete->setEnabled(hasSelection);
}
void MainWindow::OnViewNodePalette()
{
if (m_nodePalette)
{
m_nodePalette->toggleViewAction()->trigger();
}
}
void MainWindow::OnViewMiniMap()
{
if (m_minimap)
{
m_minimap->toggleViewAction()->trigger();
}
}
void MainWindow::OnViewLogWindow()
{
if (m_loggingWindow)
{
m_loggingWindow->toggleViewAction()->trigger();
}
}
void MainWindow::OnViewGraphValidation()
{
if (m_validationDockWidget)
{
m_validationDockWidget->toggleViewAction()->trigger();
}
}
void MainWindow::OnViewDebuggingWindow()
{
if (m_loggingWindow)
{
m_loggingWindow->toggleViewAction()->trigger();
}
}
void MainWindow::OnViewUnitTestManager()
{
if (m_unitTestDockWidget == nullptr)
{
CreateUnitTestWidget();
}
if (m_unitTestDockWidget)
{
m_unitTestDockWidget->show();
m_unitTestDockWidget->raise();
m_unitTestDockWidget->activateWindow();
}
}
void MainWindow::OnViewStatisticsPanel()
{
if (m_statisticsDialog)
{
m_statisticsDialog->InitStatisticsWindow();
m_statisticsDialog->show();
m_statisticsDialog->raise();
m_statisticsDialog->activateWindow();
}
}
void MainWindow::OnViewPresetsEditor()
{
if (m_presetEditor && m_presetWrapper)
{
QSize boundingBox = size();
QPointF newPosition = mapToGlobal(QPoint(aznumeric_cast<int>(boundingBox.width() * 0.5f), aznumeric_cast<int>(boundingBox.height() * 0.5f)));
m_presetEditor->show();
m_presetWrapper->show();
m_presetWrapper->raise();
m_presetWrapper->activateWindow();
QRect geometry = m_presetWrapper->geometry();
QSize originalSize = geometry.size();
newPosition.setX(newPosition.x() - geometry.width() * 0.5f);
newPosition.setY(newPosition.y() - geometry.height() * 0.5f);
geometry.setTopLeft(newPosition.toPoint());
geometry.setWidth(originalSize.width());
geometry.setHeight(originalSize.height());
m_presetWrapper->setGeometry(geometry);
}
}
void MainWindow::OnViewProperties()
{
if (m_propertyGrid)
{
m_propertyGrid->toggleViewAction()->trigger();
}
}
void MainWindow::OnViewDebugger()
{
}
void MainWindow::OnViewCommandLine()
{
if (m_commandLine->isVisible())
{
m_commandLine->hide();
}
else
{
m_commandLine->show();
}
}
void MainWindow::OnViewLog()
{
if (m_logPanel)
{
m_logPanel->toggleViewAction()->trigger();
}
}
void MainWindow::OnBookmarks()
{
if (m_bookmarkDockWidget)
{
m_bookmarkDockWidget->toggleViewAction()->trigger();
}
}
void MainWindow::OnVariableManager()
{
if (m_variableDockWidget)
{
m_variableDockWidget->toggleViewAction()->trigger();
}
}
void MainWindow::OnRestoreDefaultLayout()
{
if (!m_defaultLayout.isEmpty())
{
restoreState(m_defaultLayout);
UpdateViewMenu();
}
}
void MainWindow::UpdateViewMenu()
{
if (ui->action_ViewBookmarks->isChecked() != m_bookmarkDockWidget->isVisible())
{
QSignalBlocker signalBlocker(ui->action_ViewBookmarks);
ui->action_ViewBookmarks->setChecked(m_bookmarkDockWidget->isVisible());
}
if (ui->action_ViewMiniMap->isChecked() != m_minimap->isVisible())
{
QSignalBlocker signalBlocker(ui->action_ViewMiniMap);
ui->action_ViewMiniMap->setChecked(m_minimap->isVisible());
}
if (ui->action_ViewNodePalette->isChecked() != m_nodePalette->isVisible())
{
QSignalBlocker signalBlocker(ui->action_ViewNodePalette);
ui->action_ViewNodePalette->setChecked(m_nodePalette->isVisible());
}
if (ui->action_ViewProperties->isChecked() != m_propertyGrid->isVisible())
{
QSignalBlocker signalBlocker(ui->action_ViewProperties);
ui->action_ViewProperties->setChecked(m_propertyGrid->isVisible());
}
if (ui->action_ViewVariableManager->isChecked() != m_variableDockWidget->isVisible())
{
QSignalBlocker signalBlocker(ui->action_ViewVariableManager);
ui->action_ViewVariableManager->setChecked(m_variableDockWidget->isVisible());
}
if (ui->action_ViewLogWindow->isChecked() != m_loggingWindow->isVisible())
{
QSignalBlocker signalBlocker(ui->action_ViewLogWindow);
ui->action_ViewLogWindow->setChecked(m_loggingWindow->isVisible());
}
if (ui->action_GraphValidation->isChecked() != m_validationDockWidget->isVisible())
{
QSignalBlocker signalBlocker(ui->action_GraphValidation);
ui->action_GraphValidation->setChecked(m_validationDockWidget->isVisible());
}
if (ui->action_Debugging->isChecked() != m_loggingWindow->isVisible())
{
ui->action_Debugging->setChecked(m_loggingWindow->isVisible());
}
// Want these two elements to be mutually exclusive.
if (m_statusWidget->isVisible() == m_validationDockWidget->isVisible())
{
statusBar()->setVisible(!m_validationDockWidget->isVisible());
m_statusWidget->setVisible(!m_validationDockWidget->isVisible());
}
}
void MainWindow::DeleteNodes(const AZ::EntityId& graphCanvasGraphId, const AZStd::vector<AZ::EntityId>& nodes)
{
// clear the selection then delete the nodes that were selected
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::ClearSelection);
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::Delete, AZStd::unordered_set<AZ::EntityId>{ nodes.begin(), nodes.end() });
}
void MainWindow::DeleteConnections(const AZ::EntityId& graphCanvasGraphId, const AZStd::vector<AZ::EntityId>& connections)
{
ScopedVariableSetter<bool> scopedIgnoreSelection(m_ignoreSelection, true);
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::Delete, AZStd::unordered_set<AZ::EntityId>{ connections.begin(), connections.end() });
}
void MainWindow::DisconnectEndpoints(const AZ::EntityId& graphCanvasGraphId, const AZStd::vector<GraphCanvas::Endpoint>& endpoints)
{
AZStd::unordered_set<AZ::EntityId> connections;
for (const auto& endpoint : endpoints)
{
AZStd::vector<AZ::EntityId> endpointConnections;
GraphCanvas::SceneRequestBus::EventResult(endpointConnections, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetConnectionsForEndpoint, endpoint);
connections.insert(endpointConnections.begin(), endpointConnections.end());
}
DeleteConnections(graphCanvasGraphId, { connections.begin(), connections.end() });
}
void MainWindow::RunUpgradeTool()
{
VersionExplorer* versionExplorer = aznew VersionExplorer(this);
versionExplorer->exec();
// Manual correction
size_t assetsThatNeedManualInspection = AZ::Interface<IUpgradeRequests>::Get()->GetGraphsThatNeedManualUpgrade().size();
// If there are graphs that need manual correction, show the helper
if (assetsThatNeedManualInspection > 0)
{
UpgradeHelper* upgradeHelper = new UpgradeHelper(this);
upgradeHelper->show();
}
}
void MainWindow::OnShowValidationErrors()
{
m_userSettings->m_showValidationErrors = true;
if (!m_validationDockWidget->isVisible())
{
OnViewGraphValidation();
// If the window wasn't visible, it doesn't seem to get the signals.
// So need to manually prompt it to get the desired result
m_validationDockWidget->OnShowErrors();
}
}
void MainWindow::OnShowValidationWarnings()
{
m_userSettings->m_showValidationWarnings = true;
if (!m_validationDockWidget->isVisible())
{
OnViewGraphValidation();
// If the window wasn't visible, it doesn't seem to get the signals.
// So need to manually prompt it to get the desired result
m_validationDockWidget->OnShowWarnings();
}
}
void MainWindow::OnValidateCurrentGraph()
{
const bool displayToastNotification = false;
RunGraphValidation(displayToastNotification);
}
void MainWindow::RunGraphValidation(bool displayToastNotification)
{
m_validationDockWidget->OnRunValidator(displayToastNotification);
if (m_validationDockWidget->HasValidationIssues())
{
OpenValidationPanel();
}
}
void MainWindow::OnViewParamsChanged(const GraphCanvas::ViewParams& viewParams)
{
AZ_UNUSED(viewParams);
RestartAutoTimerSave();
}
void MainWindow::OnZoomChanged(qreal)
{
RestartAutoTimerSave();
}
void MainWindow::AfterEntitySelectionChanged(const AzToolsFramework::EntityIdList&, const AzToolsFramework::EntityIdList&)
{
UpdateAssignToSelectionState();
}
void MainWindow::UpdateMenuState(bool enabled)
{
m_validateGraphToolButton->setEnabled(enabled);
ui->menuRemove_Unused->setEnabled(enabled);
ui->action_RemoveUnusedNodes->setEnabled(enabled);
ui->action_RemoveUnusedVariables->setEnabled(enabled);
ui->action_RemoveUnusedElements->setEnabled(enabled);
ui->action_ZoomIn->setEnabled(enabled);
ui->action_ZoomOut->setEnabled(enabled);
ui->action_ZoomSelection->setEnabled(enabled);
ui->action_ShowEntireGraph->setEnabled(enabled);
ui->menuGo_To->setEnabled(enabled);
ui->action_GotoStartOfChain->setEnabled(enabled);
ui->action_GotoEndOfChain->setEnabled(enabled);
ui->actionZoom_To->setEnabled(enabled);
ui->action_EnableSelection->setEnabled(enabled);
ui->action_DisableSelection->setEnabled(enabled);
m_createFunctionOutput->setEnabled(enabled);
m_createFunctionInput->setEnabled(enabled);
// File Menu
ui->action_Close->setEnabled(enabled);
RefreshGraphPreferencesAction();
UpdateAssignToSelectionState();
UpdateUndoRedoState();
UpdateSaveState();
}
void MainWindow::OnWorkspaceRestoreStart()
{
m_isRestoringWorkspace = true;
}
void MainWindow::OnWorkspaceRestoreEnd(AZ::Data::AssetId lastFocusAsset)
{
if (m_isRestoringWorkspace)
{
m_isRestoringWorkspace = false;
if (m_queuedFocusOverride.IsValid())
{
SetActiveAsset(m_queuedFocusOverride);
m_queuedFocusOverride.SetInvalid();
}
else if (lastFocusAsset.IsValid())
{
SetActiveAsset(lastFocusAsset);
}
if (!m_activeAssetId.IsValid())
{
if (m_tabBar->count() > 0)
{
if (m_tabBar->currentIndex() != 0)
{
m_tabBar->setCurrentIndex(0);
}
else
{
SetActiveAsset(m_tabBar->FindAssetId(0));
}
}
else
{
SetActiveAsset({});
}
}
}
}
void MainWindow::UpdateAssignToSelectionState()
{
bool buttonEnabled = m_activeAssetId.IsValid();
if (buttonEnabled)
{
const Tracker::ScriptCanvasFileState& fileState = GetAssetFileState(m_activeAssetId);
if (fileState == Tracker::ScriptCanvasFileState::INVALID || fileState == Tracker::ScriptCanvasFileState::NEW || fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED)
{
buttonEnabled = false;
}
else
{
EditorGraphRequestBus::EventResult(buttonEnabled, GetActiveScriptCanvasId(), &EditorGraphRequests::IsRuntimeGraph);
}
m_assignToSelectedEntity->setEnabled(buttonEnabled);
}
else
{
m_assignToSelectedEntity->setEnabled(false);
}
}
void MainWindow::UpdateUndoRedoState()
{
bool isEnabled = false;
UndoRequestBus::EventResult(isEnabled, GetActiveScriptCanvasId(), &UndoRequests::CanUndo);
ui->action_Undo->setEnabled(isEnabled);
isEnabled = false;
UndoRequestBus::EventResult(isEnabled, GetActiveScriptCanvasId(), &UndoRequests::CanRedo);
ui->action_Redo->setEnabled(isEnabled);
}
void MainWindow::UpdateSaveState()
{
bool enabled = m_activeAssetId.IsValid();
bool isSaving = false;
bool hasModifications = false;
if (enabled)
{
Tracker::ScriptCanvasFileState fileState = GetAssetFileState(m_activeAssetId);
hasModifications = ( fileState == Tracker::ScriptCanvasFileState::MODIFIED
|| fileState == Tracker::ScriptCanvasFileState::NEW
|| fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED);
AssetTrackerRequestBus::BroadcastResult(isSaving, &AssetTrackerRequests::IsSaving, m_activeAssetId);
}
ui->action_Save->setEnabled(enabled && !isSaving && hasModifications);
ui->action_Save_As->setEnabled(enabled && !isSaving);
}
void MainWindow::CreateFunctionInput()
{
PushPreventUndoStateUpdate();
CreateFunctionDefinitionNode(-1);
PopPreventUndoStateUpdate();
PostUndoPoint(GetActiveScriptCanvasId());
}
void MainWindow::CreateFunctionOutput()
{
PushPreventUndoStateUpdate();
CreateFunctionDefinitionNode(1);
PopPreventUndoStateUpdate();
PostUndoPoint(GetActiveScriptCanvasId());
}
void MainWindow::CreateFunctionDefinitionNode(int positionOffset)
{
ScriptCanvas::ScriptCanvasId scriptCanvasId = GetActiveScriptCanvasId();
GraphCanvas::GraphId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
GraphCanvas::ViewId viewId;
GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId);
QRectF viewBounds;
GraphCanvas::ViewRequestBus::EventResult(viewBounds, viewId, &GraphCanvas::ViewRequests::GetCompleteArea);
const bool isInput = positionOffset < 0;
const AZStd::string rootName = isInput ? "New Input" : "New Output";
NodeIdPair nodeIdPair = Nodes::CreateFunctionDefinitionNode(scriptCanvasId, isInput, rootName);
GraphCanvas::SceneRequests* sceneRequests = GraphCanvas::SceneRequestBus::FindFirstHandler(graphCanvasGraphId);
if (sceneRequests == nullptr)
{
return;
}
QPointF pasteOffset = sceneRequests->SignalGenericAddPositionUseBegin();
sceneRequests->AddNode(nodeIdPair.m_graphCanvasId, GraphCanvas::ConversionUtils::QPointToVector(pasteOffset), false);
sceneRequests->SignalGenericAddPositionUseEnd();
if (!viewBounds.isEmpty())
{
QPointF topLeftPoint = viewBounds.center();
int widthOffset = aznumeric_cast<int>((viewBounds.width() * 0.5f) * positionOffset);
topLeftPoint.setX(topLeftPoint.x() + widthOffset);
QGraphicsItem* graphicsItem = nullptr;
GraphCanvas::SceneMemberUIRequestBus::EventResult(graphicsItem, nodeIdPair.m_graphCanvasId, &GraphCanvas::SceneMemberUIRequests::GetRootGraphicsItem);
GraphCanvas::NodeUIRequestBus::Event(nodeIdPair.m_graphCanvasId, &GraphCanvas::NodeUIRequests::AdjustSize);
qreal width = graphicsItem->sceneBoundingRect().width();
// If we are going negative we need to move over the width of the node.
if (positionOffset < 0)
{
topLeftPoint.setX(topLeftPoint.x() - width);
}
// Center the node.
qreal height = graphicsItem->sceneBoundingRect().height();
topLeftPoint.setY(topLeftPoint.y() - height * 0.5);
// Offset by the width step.
AZ::Vector2 minorStep = AZ::Vector2::CreateZero();
AZ::EntityId gridId;
GraphCanvas::SceneRequestBus::EventResult(gridId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetGrid);
GraphCanvas::GridRequestBus::EventResult(minorStep, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
QRectF sceneBoundaries = sceneRequests->AsQGraphicsScene()->sceneRect();
sceneBoundaries.adjust(minorStep.GetX(), minorStep.GetY(), -minorStep.GetX(), -minorStep.GetY());
topLeftPoint.setX(topLeftPoint.x() + minorStep.GetX() * positionOffset);
// Sanitizes the position of the node to ensure it's always 'visible'
while (topLeftPoint.x() + width <= sceneBoundaries.left())
{
topLeftPoint.setX(topLeftPoint.x() + width);
}
while (topLeftPoint.x() >= sceneBoundaries.right())
{
topLeftPoint.setX(topLeftPoint.x() - width);
}
while (topLeftPoint.y() + height <= sceneBoundaries.top())
{
topLeftPoint.setY(topLeftPoint.y() + height);
}
while (topLeftPoint.y() >= sceneBoundaries.bottom())
{
topLeftPoint.setY(topLeftPoint.y() - height);
}
////
GraphCanvas::GeometryRequestBus::Event(nodeIdPair.m_graphCanvasId, &GraphCanvas::GeometryRequests::SetPosition, GraphCanvas::ConversionUtils::QPointToVector(topLeftPoint));
GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::CenterOnArea, graphicsItem->sceneBoundingRect());
}
}
NodeIdPair MainWindow::ProcessCreateNodeMimeEvent(GraphCanvas::GraphCanvasMimeEvent* mimeEvent, const AZ::EntityId& graphCanvasGraphId, AZ::Vector2 nodeCreationPos)
{
if (!m_isInAutomation)
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::ClearSelection);
}
NodeIdPair retVal;
if (azrtti_istypeof<CreateNodeMimeEvent>(mimeEvent))
{
CreateNodeMimeEvent* createEvent = static_cast<CreateNodeMimeEvent*>(mimeEvent);
if (createEvent->ExecuteEvent(nodeCreationPos, nodeCreationPos, graphCanvasGraphId))
{
retVal = createEvent->GetCreatedPair();
}
}
else if (azrtti_istypeof<SpecializedCreateNodeMimeEvent>(mimeEvent))
{
SpecializedCreateNodeMimeEvent* specializedCreationEvent = static_cast<SpecializedCreateNodeMimeEvent*>(mimeEvent);
retVal = specializedCreationEvent->ConstructNode(graphCanvasGraphId, nodeCreationPos);
}
return retVal;
}
const GraphCanvas::GraphCanvasTreeItem* MainWindow::GetNodePaletteRoot() const
{
return m_nodePalette->GetTreeRoot();
}
void MainWindow::SignalAutomationBegin()
{
m_isInAutomation = true;
}
void MainWindow::SignalAutomationEnd()
{
m_isInAutomation = false;
}
void MainWindow::ForceCloseActiveAsset()
{
OnTabCloseRequest(m_tabBar->currentIndex());
}
bool MainWindow::RegisterObject(AZ::Crc32 elementId, QObject* object)
{
auto lookupIter = m_automationLookUpMap.find(elementId);
if (lookupIter != m_automationLookUpMap.end())
{
AZ_Error("ScriptCanvas", false, "Attempting to register two elements with the id %llu", (unsigned int)elementId);
return false;
}
m_automationLookUpMap[elementId] = object;
return true;
}
bool MainWindow::UnregisterObject(AZ::Crc32 elementId)
{
auto eraseCount = m_automationLookUpMap.erase(elementId);
return eraseCount > 0;
}
QObject* MainWindow::FindObject(AZ::Crc32 elementId)
{
auto lookupIter = m_automationLookUpMap.find(elementId);
if (lookupIter != m_automationLookUpMap.end())
{
return lookupIter->second;
}
return nullptr;
}
QObject* MainWindow::FindElementByName(QString elementName)
{
return findChild<QObject*>(elementName);
}
AZ::EntityId MainWindow::FindEditorNodeIdByAssetNodeId(const AZ::Data::AssetId& assetId, AZ::EntityId assetNodeId) const
{
AZ::EntityId editorEntityId;
AssetTrackerRequestBus::BroadcastResult(editorEntityId, &AssetTrackerRequests::GetEditorEntityIdFromSceneEntityId, assetId, assetNodeId);
return editorEntityId;
}
AZ::EntityId MainWindow::FindAssetNodeIdByEditorNodeId(const AZ::Data::AssetId& assetId, AZ::EntityId editorNodeId) const
{
AZ::EntityId sceneEntityId;
AssetTrackerRequestBus::BroadcastResult(sceneEntityId, &AssetTrackerRequests::GetSceneEntityIdFromEditorEntityId, assetId, editorNodeId);
return sceneEntityId;
}
GraphCanvas::Endpoint MainWindow::CreateNodeForProposalWithGroup(const AZ::EntityId& connectionId, const GraphCanvas::Endpoint& endpoint, const QPointF& scenePoint, const QPoint& screenPoint, AZ::EntityId groupTarget)
{
PushPreventUndoStateUpdate();
GraphCanvas::Endpoint retVal;
AZ::EntityId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
// Handle the special-case if we are creating a node proposal for an AZ::Event, then we show
// a small menu with only that applicable action
if (CreateAzEventHandlerSlotMenuAction::FindBehaviorMethodWithAzEventReturn(graphCanvasGraphId, endpoint.GetSlotId()))
{
GraphCanvas::EditorContextMenu menu(ScriptCanvasEditor::AssetEditorId);
menu.AddMenuAction(aznew CreateAzEventHandlerSlotMenuAction(&menu));
HandleContextMenu(menu, endpoint.GetSlotId(), screenPoint, scenePoint);
}
// For everything else, show the full scene context menu
else
{
m_sceneContextMenu->FilterForSourceSlot(graphCanvasGraphId, endpoint.GetSlotId());
m_sceneContextMenu->RefreshActions(graphCanvasGraphId, connectionId);
m_sceneContextMenu->SetupDisplayForProposal();
QAction* action = m_sceneContextMenu->exec(screenPoint);
// If the action returns null. We need to check if it was our widget, or just a close command.
if (action == nullptr)
{
GraphCanvas::GraphCanvasMimeEvent* mimeEvent = m_sceneContextMenu->GetNodePalette()->GetContextMenuEvent();
if (mimeEvent)
{
NodeIdPair finalNode = ProcessCreateNodeMimeEvent(mimeEvent, graphCanvasGraphId, AZ::Vector2(aznumeric_cast<float>(scenePoint.x()), aznumeric_cast<float>(scenePoint.y())));
if (finalNode.m_graphCanvasId.IsValid())
{
GraphCanvas::VisualRequestBus::Event(finalNode.m_graphCanvasId, &GraphCanvas::VisualRequests::SetVisible, false);
retVal = HandleProposedConnection(graphCanvasGraphId, connectionId, endpoint, finalNode.m_graphCanvasId, screenPoint);
}
if (retVal.IsValid())
{
AZStd::unordered_set<GraphCanvas::ConnectionId> createdConnections = GraphCanvas::GraphUtils::CreateOpportunisticConnectionsBetween(endpoint, retVal);
GraphCanvas::VisualRequestBus::Event(finalNode.m_graphCanvasId, &GraphCanvas::VisualRequests::SetVisible, true);
AZ::Vector2 position;
GraphCanvas::GeometryRequestBus::EventResult(position, retVal.GetNodeId(), &GraphCanvas::GeometryRequests::GetPosition);
QPointF connectionPoint;
GraphCanvas::SlotUIRequestBus::EventResult(connectionPoint, retVal.GetSlotId(), &GraphCanvas::SlotUIRequests::GetConnectionPoint);
qreal verticalOffset = connectionPoint.y() - position.GetY();
position.SetY(aznumeric_cast<float>(scenePoint.y() - verticalOffset));
qreal horizontalOffset = connectionPoint.x() - position.GetX();
position.SetX(aznumeric_cast<float>(scenePoint.x() - horizontalOffset));
GraphCanvas::GeometryRequestBus::Event(retVal.GetNodeId(), &GraphCanvas::GeometryRequests::SetPosition, position);
GraphCanvas::GraphUtils::AddElementToGroup(finalNode.m_graphCanvasId, groupTarget);
GraphCanvas::SceneNotificationBus::Event(graphCanvasGraphId, &GraphCanvas::SceneNotifications::PostCreationEvent);
}
else
{
GraphCanvas::GraphUtils::DeleteOutermostNode(graphCanvasGraphId, finalNode.m_graphCanvasId);
}
}
}
}
PopPreventUndoStateUpdate();
return retVal;
}
void MainWindow::OnWrapperNodeActionWidgetClicked(const AZ::EntityId& wrapperNode, const QRect& actionWidgetBoundingRect, const QPointF& scenePoint, const QPoint& screenPoint)
{
if (EBusHandlerNodeDescriptorRequestBus::FindFirstHandler(wrapperNode) != nullptr)
{
m_ebusHandlerActionMenu->SetEbusHandlerNode(wrapperNode);
// We don't care about the result, since the actions are done on demand with the menu
m_ebusHandlerActionMenu->exec(screenPoint);
}
else if (ScriptCanvasWrapperNodeDescriptorRequestBus::FindFirstHandler(wrapperNode) != nullptr)
{
ScriptCanvasWrapperNodeDescriptorRequestBus::Event(wrapperNode, &ScriptCanvasWrapperNodeDescriptorRequests::OnWrapperAction, actionWidgetBoundingRect, scenePoint, screenPoint);
}
}
void MainWindow::OnSelectionManipulationBegin()
{
m_ignoreSelection = true;
}
void MainWindow::OnSelectionManipulationEnd()
{
m_ignoreSelection = false;
OnSelectionChanged();
}
AZ::EntityId MainWindow::CreateNewGraph()
{
AZ::EntityId graphId;
OnFileNew();
if (m_activeAssetId.IsValid())
{
graphId = GetActiveGraphCanvasGraphId();
}
return graphId;
}
bool MainWindow::ContainsGraph(const GraphCanvas::GraphId&) const
{
return false;
}
bool MainWindow::CloseGraph(const GraphCanvas::GraphId&)
{
return false;
}
void MainWindow::CustomizeConnectionEntity(AZ::Entity* connectionEntity)
{
connectionEntity->CreateComponent<SceneMemberMappingComponent>();
}
void MainWindow::ShowAssetPresetsMenu(GraphCanvas::ConstructType constructType)
{
OnViewPresetsEditor();
if (m_presetEditor)
{
m_presetEditor->SetActiveConstructType(constructType);
}
}
//! Hook for receiving context menu events for each QGraphicsScene
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::ShowSceneContextMenuWithGroup(const QPoint& screenPoint, const QPointF& scenePoint, AZ::EntityId groupTarget)
{
bool tryDaisyChain = (QApplication::keyboardModifiers() & Qt::KeyboardModifier::ShiftModifier) != 0;
GraphCanvas::GraphId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
ScriptCanvas::ScriptCanvasId scriptCanvasGraphId = GetActiveScriptCanvasId();
if (!graphCanvasGraphId.IsValid() || !scriptCanvasGraphId.IsValid())
{
// Nothing to do.
return GraphCanvas::ContextMenuAction::SceneReaction::Nothing;
}
m_sceneContextMenu->ResetSourceSlotFilter();
m_sceneContextMenu->RefreshActions(graphCanvasGraphId, AZ::EntityId());
QAction* action = m_sceneContextMenu->exec(screenPoint);
GraphCanvas::ContextMenuAction::SceneReaction reaction = GraphCanvas::ContextMenuAction::SceneReaction::Nothing;
if (action == nullptr)
{
GraphCanvas::GraphCanvasMimeEvent* mimeEvent = m_sceneContextMenu->GetNodePalette()->GetContextMenuEvent();
NodeIdPair finalNode = ProcessCreateNodeMimeEvent(mimeEvent, graphCanvasGraphId, AZ::Vector2(aznumeric_cast<float>(scenePoint.x()), aznumeric_cast<float>(scenePoint.y())));
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::ClearSelection);
if (finalNode.m_graphCanvasId.IsValid())
{
GraphCanvas::VisualRequestBus::Event(finalNode.m_graphCanvasId, &GraphCanvas::VisualRequests::SetVisible, true);
AZ::Vector2 position;
GraphCanvas::GeometryRequestBus::EventResult(position, finalNode.m_graphCanvasId, &GraphCanvas::GeometryRequests::GetPosition);
GraphCanvas::GeometryRequestBus::Event(finalNode.m_graphCanvasId, &GraphCanvas::GeometryRequests::SetPosition, position);
// If we have a valid group target. We're going to want to add the element to the group.
GraphCanvas::GraphUtils::AddElementToGroup(finalNode.m_graphCanvasId, groupTarget);
GraphCanvas::SceneNotificationBus::Event(graphCanvasGraphId, &GraphCanvas::SceneNotifications::PostCreationEvent);
if (tryDaisyChain)
{
QTimer::singleShot(50, [graphCanvasGraphId, finalNode, screenPoint, scenePoint, groupTarget]()
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::HandleProposalDaisyChainWithGroup, finalNode.m_graphCanvasId, GraphCanvas::SlotTypes::ExecutionSlot, GraphCanvas::CT_Output, screenPoint, scenePoint, groupTarget);
});
}
}
}
else
{
GraphCanvas::ContextMenuAction* contextMenuAction = qobject_cast<GraphCanvas::ContextMenuAction*>(action);
if (contextMenuAction)
{
PushPreventUndoStateUpdate();
AZ::Vector2 mousePoint(aznumeric_cast<float>(scenePoint.x()), aznumeric_cast<float>(scenePoint.y()));
reaction = contextMenuAction->TriggerAction(graphCanvasGraphId, mousePoint);
PopPreventUndoStateUpdate();
}
}
return reaction;
}
//! Hook for receiving context menu events for each QGraphicsScene
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::ShowNodeContextMenu(const AZ::EntityId& nodeId, const QPoint& screenPoint, const QPointF& scenePoint)
{
GraphCanvas::NodeContextMenu contextMenu(ScriptCanvasEditor::AssetEditorId);
NodeDescriptorType descriptorType = NodeDescriptorType::Unknown;
NodeDescriptorRequestBus::EventResult(descriptorType, nodeId, &NodeDescriptorRequests::GetType);
if (descriptorType == NodeDescriptorType::GetVariable
|| descriptorType == NodeDescriptorType::SetVariable)
{
contextMenu.AddMenuAction(aznew ConvertVariableNodeToReferenceAction(&contextMenu));
}
if (descriptorType == NodeDescriptorType::FunctionDefinitionNode)
{
NodeDescriptorComponent* descriptor = nullptr;
NodeDescriptorRequestBus::EventResult(descriptor, nodeId, &NodeDescriptorRequests::GetDescriptorComponent);
contextMenu.AddMenuAction(aznew RenameFunctionDefinitionNodeAction(descriptor, &contextMenu));
}
return HandleContextMenu(contextMenu, nodeId, screenPoint, scenePoint);
}
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::ShowCommentContextMenu(const AZ::EntityId& nodeId, const QPoint& screenPoint, const QPointF& scenePoint)
{
GraphCanvas::CommentContextMenu contextMenu(ScriptCanvasEditor::AssetEditorId);
return HandleContextMenu(contextMenu, nodeId, screenPoint, scenePoint);
}
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::ShowNodeGroupContextMenu(const AZ::EntityId& groupId, const QPoint& screenPoint, const QPointF& scenePoint)
{
GraphCanvas::NodeGroupContextMenu contextMenu(ScriptCanvasEditor::AssetEditorId);
return HandleContextMenu(contextMenu, groupId, screenPoint, scenePoint);
}
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::ShowCollapsedNodeGroupContextMenu(const AZ::EntityId& nodeId, const QPoint& screenPoint, const QPointF& scenePoint)
{
GraphCanvas::CollapsedNodeGroupContextMenu contextMenu(ScriptCanvasEditor::AssetEditorId);
return HandleContextMenu(contextMenu, nodeId, screenPoint, scenePoint);
}
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::ShowBookmarkContextMenu(const AZ::EntityId& bookmarkId, const QPoint& screenPoint, const QPointF& scenePoint)
{
GraphCanvas::BookmarkContextMenu contextMenu(ScriptCanvasEditor::AssetEditorId);
return HandleContextMenu(contextMenu, bookmarkId, screenPoint, scenePoint);
}
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::ShowConnectionContextMenuWithGroup(const AZ::EntityId& connectionId, const QPoint& screenPoint, const QPointF& scenePoint, AZ::EntityId groupTarget)
{
PushPreventUndoStateUpdate();
GraphCanvas::ContextMenuAction::SceneReaction reaction = GraphCanvas::ContextMenuAction::SceneReaction::Nothing;
AZ::Vector2 sceneVector(aznumeric_cast<float>(scenePoint.x()), aznumeric_cast<float>(scenePoint.y()));
GraphCanvas::GraphId graphCanvasGraphId = GetActiveGraphCanvasGraphId();
m_connectionContextMenu->RefreshActions(graphCanvasGraphId, connectionId);
QAction* result = m_connectionContextMenu->exec(screenPoint);
GraphCanvas::ContextMenuAction* contextMenuAction = qobject_cast<GraphCanvas::ContextMenuAction*>(result);
// If the action returns null. We need to check if it was our widget, or just a close command.
if (contextMenuAction)
{
reaction = contextMenuAction->TriggerAction(graphCanvasGraphId, sceneVector);
}
else
{
GraphCanvas::GraphCanvasMimeEvent* mimeEvent = m_connectionContextMenu->GetNodePalette()->GetContextMenuEvent();
if (mimeEvent)
{
NodeIdPair finalNode = ProcessCreateNodeMimeEvent(mimeEvent, graphCanvasGraphId, AZ::Vector2(aznumeric_cast<float>(scenePoint.x()), aznumeric_cast<float>(scenePoint.y())));
GraphCanvas::Endpoint sourceEndpoint;
GraphCanvas::ConnectionRequestBus::EventResult(sourceEndpoint, connectionId, &GraphCanvas::ConnectionRequests::GetSourceEndpoint);
GraphCanvas::Endpoint targetEndpoint;
GraphCanvas::ConnectionRequestBus::EventResult(targetEndpoint, connectionId, &GraphCanvas::ConnectionRequests::GetTargetEndpoint);
if (finalNode.m_graphCanvasId.IsValid())
{
GraphCanvas::ConnectionSpliceConfig spliceConfig;
spliceConfig.m_allowOpportunisticConnections = true;
if (!GraphCanvas::GraphUtils::SpliceNodeOntoConnection(finalNode.m_graphCanvasId, connectionId, spliceConfig))
{
GraphCanvas::GraphUtils::DeleteOutermostNode(graphCanvasGraphId, finalNode.m_graphCanvasId);
}
else
{
reaction = GraphCanvas::ContextMenuAction::SceneReaction::PostUndo;
// Now we can deal with the alignment of the node.
GraphCanvas::VisualRequestBus::Event(finalNode.m_graphCanvasId, &GraphCanvas::VisualRequests::SetVisible, true);
AZ::Vector2 position(0,0);
GraphCanvas::GeometryRequestBus::EventResult(position, finalNode.m_graphCanvasId, &GraphCanvas::GeometryRequests::GetPosition);
QPointF sourceConnectionPoint(0,0);
GraphCanvas::SlotUIRequestBus::EventResult(sourceConnectionPoint, spliceConfig.m_splicedSourceEndpoint.GetSlotId(), &GraphCanvas::SlotUIRequests::GetConnectionPoint);
QPointF targetConnectionPoint(0,0);
GraphCanvas::SlotUIRequestBus::EventResult(targetConnectionPoint, spliceConfig.m_splicedTargetEndpoint.GetSlotId(), &GraphCanvas::SlotUIRequests::GetConnectionPoint);
// Average our two points so we splice roughly in the center of our node.
QPointF connectionPoint = (sourceConnectionPoint + targetConnectionPoint) * 0.5f;
qreal verticalOffset = connectionPoint.y() - position.GetY();
position.SetY(aznumeric_cast<float>(scenePoint.y() - verticalOffset));
qreal horizontalOffset = connectionPoint.x() - position.GetX();
position.SetX(aznumeric_cast<float>(scenePoint.x() - horizontalOffset));
GraphCanvas::GeometryRequestBus::Event(finalNode.m_graphCanvasId, &GraphCanvas::GeometryRequests::SetPosition, position);
if (IsNodeNudgingEnabled())
{
GraphCanvas::NodeNudgingController nudgingController(graphCanvasGraphId, { finalNode.m_graphCanvasId });
nudgingController.FinalizeNudging();
}
GraphCanvas::GraphUtils::AddElementToGroup(finalNode.m_graphCanvasId, groupTarget);
GraphCanvas::SceneNotificationBus::Event(graphCanvasGraphId, &GraphCanvas::SceneNotifications::PostCreationEvent);
}
}
}
}
PopPreventUndoStateUpdate();
return reaction;
}
GraphCanvas::ContextMenuAction::SceneReaction MainWindow::ShowSlotContextMenu(const AZ::EntityId& slotId, const QPoint& screenPoint, const QPointF& scenePoint)
{
GraphCanvas::SlotContextMenu contextMenu(ScriptCanvasEditor::AssetEditorId);
contextMenu.AddMenuAction(aznew ConvertReferenceToVariableNodeAction(&contextMenu));
contextMenu.AddMenuAction(aznew ExposeSlotMenuAction(&contextMenu));
contextMenu.AddMenuAction(aznew CreateAzEventHandlerSlotMenuAction(&contextMenu));
contextMenu.AddMenuAction(aznew SetDataSlotTypeMenuAction(&contextMenu));
return HandleContextMenu(contextMenu, slotId, screenPoint, scenePoint);
}
void MainWindow::OnSystemTick()
{
if (HasSystemTickAction(SystemTickActionFlag::RefreshPropertyGrid))
{
RemoveSystemTickAction(SystemTickActionFlag::RefreshPropertyGrid);
RefreshSelection();
}
if (HasSystemTickAction(SystemTickActionFlag::CloseWindow))
{
RemoveSystemTickAction(SystemTickActionFlag::CloseWindow);
qobject_cast<QWidget*>(parent())->close();
}
if (HasSystemTickAction(SystemTickActionFlag::UpdateSaveMenuState))
{
RemoveSystemTickAction(SystemTickActionFlag::UpdateSaveMenuState);
UpdateSaveState();
}
if (HasSystemTickAction(SystemTickActionFlag::CloseCurrentGraph))
{
RemoveSystemTickAction(SystemTickActionFlag::CloseCurrentGraph);
if (m_tabBar)
{
m_tabBar->tabCloseRequested(m_tabBar->currentIndex());
}
}
if (HasSystemTickAction(SystemTickActionFlag::CloseNextTabAction))
{
RemoveSystemTickAction(SystemTickActionFlag::CloseNextTabAction);
CloseNextTab();
}
if (m_systemTickActions == 0)
{
AZ::SystemTickBus::Handler::BusDisconnect();
}
}
void MainWindow::OnCommandStarted(AZ::Crc32)
{
PushPreventUndoStateUpdate();
}
void MainWindow::OnCommandFinished(AZ::Crc32)
{
PopPreventUndoStateUpdate();
}
void MainWindow::PrepareActiveAssetForSave()
{
PrepareAssetForSave(m_activeAssetId);
}
void MainWindow::PrepareAssetForSave(const AZ::Data::AssetId& assetId)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, assetId);
if (memoryAsset)
{
AZ::EntityId graphId = memoryAsset->GetGraphId();
AZ::EntityId scriptCanvasId = memoryAsset->GetScriptCanvasId();
AZ::Entity* entity = nullptr;
GraphRequestBus::EventResult(entity, scriptCanvasId, &GraphRequests::GetGraphEntity);
if (entity)
{
GraphCanvas::GraphModelRequestBus::Event(graphId, &GraphCanvas::GraphModelRequests::OnSaveDataDirtied, entity->GetId());
}
GraphCanvas::GraphModelRequestBus::Event(graphId, &GraphCanvas::GraphModelRequests::OnSaveDataDirtied, graphId);
ScriptCanvasEditor::Graph* graph = AZ::EntityUtils::FindFirstDerivedComponent<ScriptCanvasEditor::Graph>(entity);
if (graph)
{
graph->MarkVersion();
}
}
}
void MainWindow::RestartAutoTimerSave(bool forceTimer)
{
if (m_autoSaveTimer.isActive() || forceTimer)
{
m_autoSaveTimer.stop();
m_autoSaveTimer.start();
}
}
void MainWindow::OnSelectedEntitiesAboutToShow()
{
AzToolsFramework::EntityIdList selectedEntityIds;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(selectedEntityIds, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
m_selectedEntityMenu->clear();
for (const AZ::EntityId& entityId : selectedEntityIds)
{
bool isLayerEntity = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(isLayerEntity, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isLayerEntity)
{
continue;
}
AZ::NamedEntityId namedEntityId(entityId);
QAction* actionElement = new QAction(namedEntityId.GetName().data(), m_selectedEntityMenu);
QObject::connect(actionElement, &QAction::triggered, [this, entityId]() {
OnAssignToEntity(entityId);
});
m_selectedEntityMenu->addAction(actionElement);
}
}
void MainWindow::OnAssignToSelectedEntities()
{
Tracker::ScriptCanvasFileState fileState;
AssetTrackerRequestBus::BroadcastResult(fileState, &AssetTrackerRequests::GetFileState, m_activeAssetId);
bool isDocumentOpen = false;
AzToolsFramework::EditorRequests::Bus::BroadcastResult(isDocumentOpen, &AzToolsFramework::EditorRequests::IsLevelDocumentOpen);
if (fileState == Tracker::ScriptCanvasFileState::NEW || fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED || !isDocumentOpen)
{
return;
}
AzToolsFramework::EntityIdList selectedEntityIds;
AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(selectedEntityIds, &AzToolsFramework::ToolsApplicationRequests::GetSelectedEntities);
auto selectedEntityIdIter = selectedEntityIds.begin();
bool isLayerAmbiguous = false;
AZ::EntityId targetLayer;
while (selectedEntityIdIter != selectedEntityIds.end())
{
bool isLayerEntity = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(isLayerEntity, (*selectedEntityIdIter), &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isLayerEntity)
{
if (targetLayer.IsValid())
{
isLayerAmbiguous = true;
}
targetLayer = (*selectedEntityIdIter);
selectedEntityIdIter = selectedEntityIds.erase(selectedEntityIdIter);
}
else
{
++selectedEntityIdIter;
}
}
if (selectedEntityIds.empty())
{
AZ::EntityId createdId;
AzToolsFramework::EditorRequests::Bus::BroadcastResult(createdId, &AzToolsFramework::EditorRequests::CreateNewEntity, AZ::EntityId());
selectedEntityIds.emplace_back(createdId);
if (targetLayer.IsValid() && !isLayerAmbiguous)
{
AZ::TransformBus::Event(createdId, &AZ::TransformBus::Events::SetParent, targetLayer);
}
}
for (const AZ::EntityId& entityId : selectedEntityIds)
{
AssignGraphToEntityImpl(entityId);
}
}
void MainWindow::OnAssignToEntity(const AZ::EntityId& entityId)
{
Tracker::ScriptCanvasFileState fileState = GetAssetFileState(m_activeAssetId);
if (fileState == Tracker::ScriptCanvasFileState::MODIFIED
|| fileState == Tracker::ScriptCanvasFileState::UNMODIFIED)
{
AssignGraphToEntityImpl(entityId);
}
}
ScriptCanvasEditor::Tracker::ScriptCanvasFileState MainWindow::GetAssetFileState(AZ::Data::AssetId assetId) const
{
Tracker::ScriptCanvasFileState fileState = Tracker::ScriptCanvasFileState::INVALID;
AssetTrackerRequestBus::BroadcastResult(fileState, &AssetTrackerRequests::GetFileState, assetId);
return fileState;
}
void MainWindow::AssignGraphToEntityImpl(const AZ::EntityId& entityId)
{
bool isLayerEntity = false;
AzToolsFramework::Layers::EditorLayerComponentRequestBus::EventResult(isLayerEntity, entityId, &AzToolsFramework::Layers::EditorLayerComponentRequestBus::Events::HasLayer);
if (isLayerEntity)
{
return;
}
EditorScriptCanvasComponentRequests* firstRequestBus = nullptr;
EditorScriptCanvasComponentRequests* firstEmptyRequestBus = nullptr;
EditorScriptCanvasComponentRequestBus::EnumerateHandlersId(entityId, [&firstRequestBus, &firstEmptyRequestBus](EditorScriptCanvasComponentRequests* scriptCanvasRequests)
{
if (firstRequestBus == nullptr)
{
firstRequestBus = scriptCanvasRequests;
}
if (!scriptCanvasRequests->HasAssetId())
{
firstEmptyRequestBus = scriptCanvasRequests;
}
return firstRequestBus == nullptr || firstEmptyRequestBus == nullptr;
});
auto usableRequestBus = firstEmptyRequestBus;
if (usableRequestBus == nullptr)
{
usableRequestBus = firstRequestBus;
}
if (usableRequestBus == nullptr)
{
AzToolsFramework::EntityCompositionRequestBus::Broadcast(&EntityCompositionRequests::AddComponentsToEntities, AzToolsFramework::EntityIdList{ entityId }
, AZ::ComponentTypeList{ azrtti_typeid<EditorScriptCanvasComponent>() });
usableRequestBus = EditorScriptCanvasComponentRequestBus::FindFirstHandler(entityId);
}
if (usableRequestBus)
{
ScriptCanvasMemoryAsset::pointer memoryAsset;
AssetTrackerRequestBus::BroadcastResult(memoryAsset, &AssetTrackerRequests::GetAsset, m_activeAssetId);
if (memoryAsset)
{
// We need to assign the AssetId for the file asset, not the in-memory asset
usableRequestBus->SetAssetId(memoryAsset->GetFileAssetId());
}
}
}
bool MainWindow::HasSystemTickAction(SystemTickActionFlag action)
{
return (m_systemTickActions & action) != 0;
}
void MainWindow::RemoveSystemTickAction(SystemTickActionFlag action)
{
m_systemTickActions = m_systemTickActions & (~action);
}
void MainWindow::AddSystemTickAction(SystemTickActionFlag action)
{
if (!AZ::SystemTickBus::Handler::BusIsConnected())
{
AZ::SystemTickBus::Handler::BusConnect();
}
m_systemTickActions |= action;
}
void MainWindow::BlockCloseRequests()
{
m_queueCloseRequest = true;
}
void MainWindow::UnblockCloseRequests()
{
if (m_queueCloseRequest)
{
m_queueCloseRequest = false;
if (m_hasQueuedClose)
{
qobject_cast<QWidget*>(parent())->close();
}
}
}
void MainWindow::OpenNextFile()
{
if (!m_filesToOpen.empty())
{
QString nextFile = m_filesToOpen.front();
m_filesToOpen.pop_front();
OpenFile(nextFile.toUtf8().data());
}
else
{
m_errorFilePath.clear();
}
}
double MainWindow::GetSnapDistance() const
{
if (m_userSettings)
{
return m_userSettings->m_snapDistance;
}
return 10.0;
}
bool MainWindow::IsGroupDoubleClickCollapseEnabled() const
{
if (m_userSettings)
{
return m_userSettings->m_enableGroupDoubleClickCollapse;
}
return true;
}
bool MainWindow::IsBookmarkViewportControlEnabled() const
{
if (m_userSettings)
{
return m_userSettings->m_allowBookmarkViewpointControl;
}
return false;
}
bool MainWindow::IsDragNodeCouplingEnabled() const
{
if (m_userSettings)
{
return m_userSettings->m_dragNodeCouplingConfig.m_enabled;
}
return false;
}
AZStd::chrono::milliseconds MainWindow::GetDragCouplingTime() const
{
if (m_userSettings)
{
return AZStd::chrono::milliseconds(m_userSettings->m_dragNodeCouplingConfig.m_timeMS);
}
return AZStd::chrono::milliseconds(500);
}
bool MainWindow::IsDragConnectionSpliceEnabled() const
{
if (m_userSettings)
{
return m_userSettings->m_dragNodeSplicingConfig.m_enabled;
}
return false;
}
AZStd::chrono::milliseconds MainWindow::GetDragConnectionSpliceTime() const
{
if (m_userSettings)
{
return AZStd::chrono::milliseconds(m_userSettings->m_dragNodeSplicingConfig.m_timeMS);
}
return AZStd::chrono::milliseconds(500);
}
bool MainWindow::IsDropConnectionSpliceEnabled() const
{
if (m_userSettings)
{
return m_userSettings->m_dropNodeSplicingConfig.m_enabled;
}
return false;
}
AZStd::chrono::milliseconds MainWindow::GetDropConnectionSpliceTime() const
{
if (m_userSettings)
{
return AZStd::chrono::milliseconds(m_userSettings->m_dropNodeSplicingConfig.m_timeMS);
}
return AZStd::chrono::milliseconds(500);
}
bool MainWindow::IsNodeNudgingEnabled() const
{
if (m_userSettings)
{
return m_userSettings->m_allowNodeNudging;
}
return false;
}
bool MainWindow::IsShakeToDespliceEnabled() const
{
if (m_userSettings)
{
return m_userSettings->m_shakeDespliceConfig.m_enabled;
}
return false;
}
int MainWindow::GetShakesToDesplice() const
{
if (m_userSettings)
{
return m_userSettings->m_shakeDespliceConfig.m_shakeCount;
}
return 3;
}
float MainWindow::GetMinimumShakePercent() const
{
if (m_userSettings)
{
return m_userSettings->m_shakeDespliceConfig.GetMinimumShakeLengthPercent();
}
return 0.03f;
}
float MainWindow::GetShakeDeadZonePercent() const
{
if (m_userSettings)
{
return m_userSettings->m_shakeDespliceConfig.GetDeadZonePercent();
}
return 0.01f;
}
float MainWindow::GetShakeStraightnessPercent() const
{
if (m_userSettings)
{
return m_userSettings->m_shakeDespliceConfig.GetStraightnessPercent();
}
return 0.75f;
}
AZStd::chrono::milliseconds MainWindow::GetMaximumShakeDuration() const
{
if (m_userSettings)
{
return AZStd::chrono::milliseconds(m_userSettings->m_shakeDespliceConfig.m_maximumShakeTimeMS);
}
return AZStd::chrono::milliseconds(500);
}
AZStd::chrono::milliseconds MainWindow::GetAlignmentTime() const
{
if (m_userSettings)
{
return AZStd::chrono::milliseconds(m_userSettings->m_alignmentTimeMS);
}
return AZStd::chrono::milliseconds(250);
}
float MainWindow::GetMaxZoom() const
{
if (m_userSettings)
{
return m_userSettings->m_zoomSettings.GetMaxZoom();
}
return 2.0f;
}
float MainWindow::GetEdgePanningPercentage() const
{
if (m_userSettings)
{
return m_userSettings->m_edgePanningSettings.GetEdgeScrollPercent();
}
return 0.1f;
}
float MainWindow::GetEdgePanningScrollSpeed() const
{
if (m_userSettings)
{
return m_userSettings->m_edgePanningSettings.GetEdgeScrollSpeed();
}
return 100.0f;
}
GraphCanvas::EditorConstructPresets* MainWindow::GetConstructPresets() const
{
if (m_userSettings)
{
return &m_userSettings->m_constructPresets;
}
return nullptr;
}
const GraphCanvas::ConstructTypePresetBucket* MainWindow::GetConstructTypePresetBucket(GraphCanvas::ConstructType constructType) const
{
GraphCanvas::EditorConstructPresets* presets = GetConstructPresets();
if (presets)
{
return presets->FindPresetBucket(constructType);
}
return nullptr;
}
GraphCanvas::Styling::ConnectionCurveType MainWindow::GetConnectionCurveType() const
{
if (m_userSettings)
{
return m_userSettings->m_stylingSettings.GetConnectionCurveType();
}
return GraphCanvas::Styling::ConnectionCurveType::Straight;
}
GraphCanvas::Styling::ConnectionCurveType MainWindow::GetDataConnectionCurveType() const
{
if (m_userSettings)
{
return m_userSettings->m_stylingSettings.GetDataConnectionCurveType();
}
return GraphCanvas::Styling::ConnectionCurveType::Straight;
}
bool MainWindow::AllowNodeDisabling() const
{
return true;
}
bool MainWindow::AllowDataReferenceSlots() const
{
return true;
}
void MainWindow::CreateUnitTestWidget()
{
// Dock Widget will be unable to dock with this as it doesn't have a parent.
// Going to orphan this as a floating window to more mimic its behavior as a pop-up window rather then a dock widget.
m_unitTestDockWidget = aznew UnitTestDockWidget(this);
m_unitTestDockWidget->setObjectName("TestManager");
m_unitTestDockWidget->setAllowedAreas(Qt::NoDockWidgetArea);
m_unitTestDockWidget->setFloating(true);
m_unitTestDockWidget->hide();
// Restore this if we want the dock widget to again be a toggleable thing.
//connect(m_unitTestDockWidget, &QDockWidget::visibilityChanged, this, &MainWindow::OnViewVisibilityChanged);
}
void MainWindow::DisableAssetView(ScriptCanvasMemoryAsset::pointer memoryAsset)
{
if (memoryAsset->GetView())
{
memoryAsset->GetView()->DisableView();
}
m_tabBar->setEnabled(false);
m_bookmarkDockWidget->setEnabled(false);
m_variableDockWidget->setEnabled(false);
m_propertyGrid->DisableGrid();
m_editorToolbar->OnViewDisabled();
m_createFunctionInput->setEnabled(false);
m_createFunctionOutput->setEnabled(false);
m_createScriptCanvas->setEnabled(false);
UpdateMenuState(false);
ui->action_New_Script->setEnabled(false);
m_autoSaveTimer.stop();
}
void MainWindow::EnableAssetView(ScriptCanvasMemoryAsset::pointer memoryAsset)
{
if (memoryAsset->GetView())
{
memoryAsset->GetView()->EnableView();
}
m_tabBar->setEnabled(true);
m_bookmarkDockWidget->setEnabled(true);
m_variableDockWidget->setEnabled(true);
m_propertyGrid->EnableGrid();
m_editorToolbar->OnViewEnabled();
m_createScriptCanvas->setEnabled(true);
ui->action_New_Script->setEnabled(true);
UpdateMenuState(true);
UpdateUndoRedoState();
}
#include <Editor/View/Windows/moc_MainWindow.cpp>
}