You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4508 lines
166 KiB
C++
4508 lines
166 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <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 <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/Settings/SettingsRegistryMergeUtils.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/FileDialog.h>
|
|
#include <AzQtComponents/Components/Widgets/TabWidget.h>
|
|
|
|
#include <ScriptCanvas/Core/ScriptCanvasBus.h>
|
|
#include <ScriptCanvas/Core/Graph.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 <ScriptCanvas/Assets/ScriptCanvasFileHandling.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 <ScriptCanvas/Asset/AssetDescription.h>
|
|
#include <ScriptCanvas/Components/EditorScriptCanvasComponent.h>
|
|
|
|
|
|
#include <Editor/QtMetaTypes.h>
|
|
#include <GraphCanvas/Components/SceneBus.h>
|
|
|
|
#include <Editor/LyViewPaneNames.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;
|
|
ScriptCanvasEditor::SourceHandle focusedAssetId = tabBar->FindAssetId(tabBar->currentIndex());
|
|
|
|
if (m_rememberOpenCanvases)
|
|
{
|
|
activeAssets.reserve(tabBar->count());
|
|
|
|
for (int i = 0; i < tabBar->count(); ++i)
|
|
{
|
|
ScriptCanvasEditor::SourceHandle assetId = tabBar->FindAssetId(i);
|
|
|
|
const Tracker::ScriptCanvasFileState& fileState = m_mainWindow->GetAssetFileState(assetId);
|
|
|
|
if (fileState == Tracker::ScriptCanvasFileState::MODIFIED || fileState == Tracker::ScriptCanvasFileState::UNMODIFIED)
|
|
{
|
|
ScriptCanvasEditor::SourceHandle sourceId = GetSourceAssetId(assetId);
|
|
if (sourceId.IsGraphValid())
|
|
{
|
|
EditorSettings::EditorWorkspace::WorkspaceAssetSaveData assetSaveData;
|
|
assetSaveData.m_assetId = sourceId;
|
|
|
|
activeAssets.push_back(assetSaveData);
|
|
}
|
|
}
|
|
else if (assetId.AnyEquals(focusedAssetId))
|
|
{
|
|
focusedAssetId.Clear();
|
|
}
|
|
}
|
|
|
|
// The assetId needs to be the file AssetId to restore the workspace
|
|
if (focusedAssetId.IsGraphValid())
|
|
{
|
|
focusedAssetId = GetSourceAssetId(focusedAssetId);
|
|
}
|
|
|
|
// If our currently focused asset won't be restored, just show the first element.
|
|
if (!focusedAssetId.IsGraphValid())
|
|
{
|
|
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(ScriptCanvasEditor::SourceHandle());
|
|
}
|
|
else
|
|
{
|
|
m_mainWindow->OnWorkspaceRestoreStart();
|
|
}
|
|
|
|
m_queuedAssetFocus = workspace->GetFocusedAssetId();
|
|
|
|
// #sc-asset-editor
|
|
//for (const auto& assetSaveData : workspace->GetActiveAssetData())
|
|
{
|
|
// load all the files
|
|
// AssetTrackerNotificationBus::MultiHandler::BusConnect(assetSaveData.m_assetId);
|
|
//
|
|
// Callbacks::OnAssetReadyCallback onAssetReady = [this, 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 = ScriptCanvasEditor::SourceHandle();
|
|
// }
|
|
//
|
|
// 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 = ScriptCanvasEditor::SourceHandle();
|
|
// }
|
|
//
|
|
// SignalAssetComplete(assetSaveData.m_assetId);
|
|
// }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_mainWindow->OnWorkspaceRestoreEnd(ScriptCanvasEditor::SourceHandle());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Workspace::SignalAssetComplete(const ScriptCanvasEditor::SourceHandle& /*fileAssetId*/)
|
|
{
|
|
// When we are done loading all assets we can safely set the focus to the recorded asset
|
|
// auto it = AZStd::find(m_loadingAssets.begin(), m_loadingAssets.end(), fileAssetId);
|
|
// if (it != m_loadingAssets.end())
|
|
// {
|
|
// m_loadingAssets.erase(it);
|
|
// }
|
|
//
|
|
// if (m_loadingAssets.empty())
|
|
// {
|
|
// m_mainWindow->OnWorkspaceRestoreEnd(m_queuedAssetFocus);
|
|
// m_queuedAssetFocus.SetInvalid();
|
|
// }
|
|
}
|
|
|
|
ScriptCanvasEditor::SourceHandle Workspace::GetSourceAssetId(const ScriptCanvasEditor::SourceHandle& memoryAssetId) const
|
|
{
|
|
return memoryAssetId;
|
|
}
|
|
|
|
////////////////
|
|
// 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")
|
|
{
|
|
AZ_PROFILE_FUNCTION(ScriptCanvas);
|
|
|
|
VariablePaletteRequestBus::Handler::BusConnect();
|
|
GraphCanvas::AssetEditorAutomationRequestBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
|
|
|
|
AZStd::array<char, AZ::IO::MaxPathLength> unresolvedPath;
|
|
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@products@/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(ScriptCanvas::SubgraphInterfaceAssetDescription().GetGroupImpl());
|
|
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(":/ScriptCanvasEditorResources/Resources/create_graph.png"));
|
|
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);
|
|
|
|
// Screenshot
|
|
{
|
|
m_takeScreenshot = new QToolButton();
|
|
m_takeScreenshot->setToolTip("Captures a full resolution screenshot of the entire graph or selected nodes into the clipboard");
|
|
m_takeScreenshot->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/scriptcanvas_screenshot.png"));
|
|
m_takeScreenshot->setEnabled(false);
|
|
}
|
|
|
|
m_editorToolbar->AddCustomAction(m_takeScreenshot);
|
|
connect(m_takeScreenshot, &QToolButton::clicked, this, &MainWindow::OnScreenshot);
|
|
|
|
|
|
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_activeGraph.IsGraphValid())
|
|
{
|
|
m_queuedFocusOverride = m_activeGraph;
|
|
}
|
|
|
|
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();
|
|
AzToolsFramework::AssetSystemBus::Handler::BusConnect();
|
|
ScriptCanvas::ScriptCanvasSettingsRequestBus::Handler::BusConnect();
|
|
AZ::SystemTickBus::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);
|
|
}
|
|
|
|
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();
|
|
AzToolsFramework::AssetSystemBus::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);
|
|
connect(ui->action_ViewMiniMap, &QAction::triggered, this, &MainWindow::OnViewMiniMap);
|
|
|
|
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(ScriptCanvasEditor::SourceHandle assetId)
|
|
{
|
|
AZ::EntityId graphId;
|
|
if (assetId.IsGraphValid())
|
|
{
|
|
EditorGraphRequestBus::EventResult(graphId, assetId.Get()->GetScriptCanvasId(), &EditorGraphRequests::GetGraphCanvasGraphId);
|
|
}
|
|
|
|
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);
|
|
|
|
if (viewId.IsValid())
|
|
{
|
|
GraphCanvas::ViewNotificationBus::Handler::BusDisconnect();
|
|
GraphCanvas::ViewNotificationBus::Handler::BusConnect(viewId);
|
|
|
|
enabled = true;
|
|
}
|
|
else
|
|
{
|
|
AZ_Error("ScriptCanvasEditor", viewId.IsValid(), "SceneRequest must return a valid ViewId");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
for (int tabCounter = 0; tabCounter < m_tabBar->count(); ++tabCounter)
|
|
{
|
|
ScriptCanvasEditor::SourceHandle assetId = m_tabBar->FindAssetId(tabCounter);
|
|
|
|
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)
|
|
{
|
|
SaveAssetImpl(assetId, Save::InPlace);
|
|
event->ignore();
|
|
return;
|
|
}
|
|
else if (shouldSaveResults == UnsavedChangesOptions::CANCEL_WITHOUT_SAVING)
|
|
{
|
|
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();
|
|
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_activeGraph);
|
|
|
|
m_propertyGrid->ClearSelection();
|
|
GeneralEditorNotificationBus::Event(GetActiveScriptCanvasId(), &GeneralEditorNotifications::OnUndoRedoEnd);
|
|
}
|
|
|
|
void MainWindow::TriggerRedo()
|
|
{
|
|
GeneralEditorNotificationBus::Event(GetActiveScriptCanvasId(), &GeneralEditorNotifications::OnUndoRedoBegin);
|
|
DequeuePropertyGridUpdate();
|
|
|
|
UndoRequestBus::Event(GetActiveScriptCanvasId(), &UndoRequests::Redo);
|
|
SignalSceneDirty(m_activeGraph);
|
|
|
|
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");
|
|
UpdateFileState(m_activeGraph, Tracker::ScriptCanvasFileState::MODIFIED);
|
|
}
|
|
|
|
const bool forceTimer = true;
|
|
RestartAutoTimerSave(forceTimer);
|
|
}
|
|
|
|
void MainWindow::SourceFileChanged
|
|
( [[maybe_unused]] AZStd::string relativePath
|
|
, AZStd::string scanFolder
|
|
, [[maybe_unused]] AZ::Uuid fileAssetId)
|
|
{
|
|
auto handle = CompleteDescription(SourceHandle(nullptr, fileAssetId, {}));
|
|
if (handle)
|
|
{
|
|
if (!IsRecentSave(*handle))
|
|
{
|
|
UpdateFileState(*handle, Tracker::ScriptCanvasFileState::MODIFIED);
|
|
}
|
|
else
|
|
{
|
|
AZ_TracePrintf
|
|
( "ScriptCanvas"
|
|
, "Ignoring source file modification notification (possibly external), as a it was recently saved by the editor: %s"
|
|
, relativePath.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::SourceFileRemoved
|
|
( AZStd::string relativePath
|
|
, [[maybe_unused]] AZStd::string scanFolder
|
|
, AZ::Uuid fileAssetId)
|
|
{
|
|
SourceHandle handle(nullptr, fileAssetId, relativePath);
|
|
{
|
|
if (!IsRecentSave(handle))
|
|
{
|
|
UpdateFileState(handle, Tracker::ScriptCanvasFileState::SOURCE_REMOVED);
|
|
}
|
|
else
|
|
{
|
|
AZ_TracePrintf
|
|
( "ScriptCanvas"
|
|
, "Ignoring source file removed notification (possibly external), as a it was recently saved by the editor: %s"
|
|
, relativePath.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::SignalSceneDirty(ScriptCanvasEditor::SourceHandle assetId)
|
|
{
|
|
UpdateFileState(assetId, Tracker::ScriptCanvasFileState::MODIFIED);
|
|
}
|
|
|
|
void MainWindow::PushPreventUndoStateUpdate()
|
|
{
|
|
++m_preventUndoStateUpdateCount;
|
|
}
|
|
|
|
void MainWindow::PopPreventUndoStateUpdate()
|
|
{
|
|
if (m_preventUndoStateUpdateCount > 0)
|
|
{
|
|
--m_preventUndoStateUpdateCount;
|
|
}
|
|
}
|
|
|
|
void MainWindow::ClearPreventUndoStateUpdate()
|
|
{
|
|
m_preventUndoStateUpdateCount = 0;
|
|
}
|
|
|
|
void MainWindow::UpdateFileState(const ScriptCanvasEditor::SourceHandle& assetId, Tracker::ScriptCanvasFileState fileState)
|
|
{
|
|
m_tabBar->UpdateFileState(assetId, fileState);
|
|
}
|
|
|
|
AZ::Outcome<int, AZStd::string> MainWindow::OpenScriptCanvasAssetId(const ScriptCanvasEditor::SourceHandle& fileAssetId, Tracker::ScriptCanvasFileState fileState)
|
|
{
|
|
if (fileAssetId.Id().IsNull())
|
|
{
|
|
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);
|
|
}
|
|
|
|
auto loadedGraphOutcome = LoadFromFile(fileAssetId.Path().c_str());
|
|
if (!loadedGraphOutcome.IsSuccess())
|
|
{
|
|
return AZ::Failure(AZStd::string::format("Failed to load graph at %s", fileAssetId.Path().c_str()));
|
|
}
|
|
|
|
auto loadedGraph = loadedGraphOutcome.TakeValue();
|
|
CompleteDescriptionInPlace(loadedGraph);
|
|
outTabIndex = CreateAssetTab(loadedGraph, fileState);
|
|
|
|
if (outTabIndex >= 0)
|
|
{
|
|
AddRecentFile(loadedGraph.Path().c_str());
|
|
OpenScriptCanvasAssetImplementation(loadedGraph, fileState);
|
|
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::OpenScriptCanvasAssetImplementation(const SourceHandle& scriptCanvasAsset, Tracker::ScriptCanvasFileState fileState, int tabIndex)
|
|
{
|
|
const ScriptCanvasEditor::SourceHandle& fileAssetId = scriptCanvasAsset;
|
|
if (!fileAssetId.IsDescriptionValid())
|
|
{
|
|
return AZ::Failure(AZStd::string("Unable to open asset with invalid asset id"));
|
|
}
|
|
|
|
if (!m_isRestoringWorkspace)
|
|
{
|
|
SetActiveAsset(scriptCanvasAsset);
|
|
}
|
|
|
|
if (!scriptCanvasAsset.IsDescriptionValid())
|
|
{
|
|
if (!m_isRestoringWorkspace)
|
|
{
|
|
AZStd::string errorPath = scriptCanvasAsset.Path().c_str();
|
|
|
|
if (errorPath.empty())
|
|
{
|
|
errorPath = m_errorFilePath;
|
|
}
|
|
|
|
if (m_queuedFocusOverride.AnyEquals(fileAssetId))
|
|
{
|
|
m_queuedFocusOverride = fileAssetId;
|
|
}
|
|
|
|
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, fileState, 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"
|
|
, fileAssetId.ToString().c_str()));
|
|
}
|
|
|
|
AZStd::string assetPath = scriptCanvasAsset.Path().c_str();
|
|
if (!assetPath.empty() && !m_loadingNewlySavedFile)
|
|
{
|
|
AddRecentFile(assetPath.c_str());
|
|
}
|
|
|
|
GraphCanvas::GraphId graphCanvasGraphId = GetGraphCanvasGraphId(scriptCanvasAsset.Get()->GetScriptCanvasId());
|
|
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId, &GraphCanvas::AssetEditorNotifications::OnGraphLoaded, graphCanvasGraphId);
|
|
GeneralAssetNotificationBus::Event(fileAssetId, &GeneralAssetNotifications::OnAssetVisualized);
|
|
return AZ::Success(outTabIndex);
|
|
}
|
|
|
|
AZ::Outcome<int, AZStd::string> MainWindow::OpenScriptCanvasAsset(ScriptCanvasEditor::SourceHandle scriptCanvasAssetId, Tracker::ScriptCanvasFileState fileState, int tabIndex)
|
|
{
|
|
if (scriptCanvasAssetId.IsGraphValid())
|
|
{
|
|
return OpenScriptCanvasAssetImplementation(scriptCanvasAssetId, fileState, tabIndex);
|
|
}
|
|
else
|
|
{
|
|
return OpenScriptCanvasAssetId(scriptCanvasAssetId, fileState);
|
|
}
|
|
}
|
|
|
|
int MainWindow::CreateAssetTab(const ScriptCanvasEditor::SourceHandle& assetId, Tracker::ScriptCanvasFileState fileState, int tabIndex)
|
|
{
|
|
return m_tabBar->InsertGraphTab(tabIndex, assetId, fileState);
|
|
}
|
|
|
|
void MainWindow::RemoveScriptCanvasAsset(const ScriptCanvasEditor::SourceHandle& assetId)
|
|
{
|
|
AssetHelpers::PrintInfo("RemoveScriptCanvasAsset : %s", assetId.ToString().c_str());
|
|
m_assetCreationRequests.erase(assetId);
|
|
GeneralAssetNotificationBus::Event(assetId, &GeneralAssetNotifications::OnAssetUnloaded);
|
|
|
|
if (assetId.IsGraphValid())
|
|
{
|
|
// Disconnect scene and asset editor buses
|
|
GraphCanvas::SceneNotificationBus::MultiHandler::BusDisconnect(assetId.Get()->GetScriptCanvasId());
|
|
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId
|
|
, &GraphCanvas::AssetEditorNotifications::OnGraphUnloaded, assetId.Get()->GetGraphCanvasGraphId());
|
|
}
|
|
|
|
int tabIndex = m_tabBar->FindTab(assetId);
|
|
QVariant tabdata = m_tabBar->tabData(tabIndex);
|
|
if (tabdata.isValid())
|
|
{
|
|
auto tabAssetId = tabdata.value<Widget::GraphTabMetadata>();
|
|
SetActiveAsset(tabAssetId.m_assetId);
|
|
}
|
|
}
|
|
|
|
int MainWindow::CloseScriptCanvasAsset(const ScriptCanvasEditor::SourceHandle& 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, Tracker::ScriptCanvasFileState::NEW).IsSuccess();
|
|
}
|
|
}
|
|
|
|
ScriptCanvasEditor::SourceHandle previousAssetId = m_activeGraph;
|
|
|
|
OnFileNew();
|
|
|
|
bool createdNewAsset = !(m_activeGraph.AnyEquals(previousAssetId));
|
|
|
|
if (createdNewAsset)
|
|
{
|
|
m_assetCreationRequests[m_activeGraph] = requestingEntityId;
|
|
}
|
|
|
|
if (m_isRestoringWorkspace)
|
|
{
|
|
m_queuedFocusOverride = m_activeGraph;
|
|
}
|
|
|
|
return createdNewAsset;
|
|
}
|
|
|
|
bool MainWindow::IsScriptCanvasAssetOpen(const ScriptCanvasEditor::SourceHandle& assetId) const
|
|
{
|
|
return m_tabBar->FindTab(assetId) >= 0;
|
|
}
|
|
|
|
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::OpenFile(const char* fullPath)
|
|
{
|
|
auto tabIndex = m_tabBar->FindTabByPath(fullPath);
|
|
if (tabIndex.IsGraphValid())
|
|
{
|
|
SetActiveAsset(tabIndex);
|
|
return;
|
|
}
|
|
|
|
AZStd::string watchFolder;
|
|
AZ::Data::AssetInfo assetInfo;
|
|
bool sourceInfoFound{};
|
|
AzToolsFramework::AssetSystemRequestBus::BroadcastResult
|
|
( sourceInfoFound
|
|
, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, fullPath, assetInfo, watchFolder);
|
|
|
|
if (!sourceInfoFound)
|
|
{
|
|
QMessageBox::warning(this, "Invalid Source Asset", QString("'%1' is not a valid asset path.").arg(fullPath), QMessageBox::Ok);
|
|
m_errorFilePath = fullPath;
|
|
AZ_Warning("ScriptCanvas", false, "Unable to open file as a ScriptCanvas graph: %s", fullPath);
|
|
return;
|
|
}
|
|
|
|
AZ::Outcome<ScriptCanvasEditor::SourceHandle, AZStd::string> outcome = LoadFromFile(fullPath);
|
|
if (!outcome.IsSuccess())
|
|
{
|
|
QMessageBox::warning(this, "Invalid Source File"
|
|
, QString("'%1' failed to load properly.\nFailure: %2").arg(fullPath).arg(outcome.GetError().c_str()), QMessageBox::Ok);
|
|
m_errorFilePath = fullPath;
|
|
AZ_Warning("ScriptCanvas", false, "Unable to open file as a ScriptCanvas graph: %s. Failure: %s"
|
|
, fullPath, outcome.GetError().c_str());
|
|
return;
|
|
}
|
|
|
|
m_errorFilePath.clear();
|
|
auto activeGraph = ScriptCanvasEditor::SourceHandle(outcome.TakeValue(), assetInfo.m_assetId.m_guid, fullPath);
|
|
|
|
auto openOutcome = OpenScriptCanvasAsset(activeGraph, Tracker::ScriptCanvasFileState::UNMODIFIED);
|
|
if (openOutcome)
|
|
{
|
|
RunGraphValidation(false);
|
|
SetActiveAsset(activeGraph);
|
|
SetRecentAssetId(activeGraph);
|
|
}
|
|
else
|
|
{
|
|
AZ_Warning("Script Canvas", openOutcome, "%s", openOutcome.GetError().data());
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
static int scriptCanvasEditorDefaultNewNameCount = 0;
|
|
|
|
AZStd::string assetPath;
|
|
|
|
for (;;)
|
|
{
|
|
AZStd::string newAssetName = AZStd::string::format(SourceDescription::GetAssetNamePattern()
|
|
, ++scriptCanvasEditorDefaultNewNameCount);
|
|
|
|
AZStd::array<char, AZ::IO::MaxPathLength> assetRootArray;
|
|
if (!AZ::IO::FileIOBase::GetInstance()->ResolvePath(SourceDescription::GetSuggestedSavePath()
|
|
, assetRootArray.data(), assetRootArray.size()))
|
|
{
|
|
AZ_ErrorOnce("Script Canvas", false, "Unable to resolve @projectroot@ path");
|
|
}
|
|
|
|
AzFramework::StringFunc::Path::Join(assetRootArray.data(), (newAssetName + SourceDescription::GetFileExtension()).data(), assetPath);
|
|
AZ::Data::AssetInfo assetInfo;
|
|
|
|
if (!AssetHelpers::GetAssetInfo(assetPath, assetInfo))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto createOutcome = CreateScriptCanvasAsset(assetPath);
|
|
if (!createOutcome.IsSuccess())
|
|
{
|
|
AZ_Warning("Script Canvas", createOutcome, "%s", createOutcome.GetError().data());
|
|
}
|
|
}
|
|
|
|
int MainWindow::InsertTabForAsset(AZStd::string_view assetPath, ScriptCanvasEditor::SourceHandle assetId, int tabIndex)
|
|
{
|
|
int outTabIndex = -1;
|
|
|
|
{
|
|
// Insert tab block
|
|
AZStd::string tabName;
|
|
AzFramework::StringFunc::Path::GetFileName(assetPath.data(), tabName);
|
|
m_tabBar->InsertGraphTab(tabIndex, assetId, Tracker::ScriptCanvasFileState::NEW);
|
|
|
|
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", assetId.ToString().c_str()).c_str());
|
|
return -1;
|
|
}
|
|
|
|
m_tabBar->setTabToolTip(outTabIndex, assetPath.data());
|
|
}
|
|
|
|
return outTabIndex;
|
|
}
|
|
|
|
void MainWindow::UpdateUndoCache(ScriptCanvasEditor::SourceHandle)
|
|
{
|
|
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, int tabIndex)
|
|
{
|
|
int outTabIndex = -1;
|
|
|
|
ScriptCanvas::DataPtr graph = EditorGraph::Create();
|
|
AZ::Uuid assetId = AZ::Uuid::CreateRandom();
|
|
ScriptCanvasEditor::SourceHandle handle = ScriptCanvasEditor::SourceHandle(graph, assetId, assetPath);
|
|
|
|
outTabIndex = InsertTabForAsset(assetPath, handle, tabIndex);
|
|
|
|
if (outTabIndex == -1)
|
|
{
|
|
return AZ::Failure(AZStd::string::format("Script Canvas Asset %.*s is not open in a tab"
|
|
, static_cast<int>(assetPath.size()), assetPath.data()));
|
|
}
|
|
|
|
SetActiveAsset(handle);
|
|
PushPreventUndoStateUpdate();
|
|
|
|
AZ::EntityId scriptCanvasEntityId = graph->GetGraph()->GetScriptCanvasId();
|
|
GraphCanvas::SceneNotificationBus::MultiHandler::BusDisconnect(scriptCanvasEntityId);
|
|
AZ::EntityId graphCanvasGraphId = GetGraphCanvasGraphId(scriptCanvasEntityId);
|
|
|
|
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId
|
|
, &GraphCanvas::AssetEditorNotifications::OnGraphRefreshed, graphCanvasGraphId, graphCanvasGraphId);
|
|
|
|
if (IsTabOpen(handle, tabIndex))
|
|
{
|
|
AZStd::string tabName;
|
|
AzFramework::StringFunc::Path::GetFileName(assetPath.data(), tabName);
|
|
m_tabBar->setTabToolTip(tabIndex, assetPath.data());
|
|
m_tabBar->SetTabText(tabIndex, tabName.c_str(), Tracker::ScriptCanvasFileState::NEW);
|
|
}
|
|
|
|
if (graphCanvasGraphId.IsValid())
|
|
{
|
|
GraphCanvas::SceneNotificationBus::MultiHandler::BusConnect(graphCanvasGraphId);
|
|
GraphCanvas::SceneMimeDelegateRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneMimeDelegateRequests::AddDelegate, m_entityMimeDelegateId);
|
|
|
|
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::SetMimeType, Widget::NodePaletteDockWidget::GetMimeType());
|
|
GraphCanvas::SceneMemberNotificationBus::Event(graphCanvasGraphId, &GraphCanvas::SceneMemberNotifications::OnSceneReady);
|
|
}
|
|
|
|
if (IsTabOpen(handle, outTabIndex))
|
|
{
|
|
RefreshActiveAsset();
|
|
}
|
|
|
|
PopPreventUndoStateUpdate();
|
|
|
|
|
|
GraphCanvas::AssetEditorNotificationBus::Event(ScriptCanvasEditor::AssetEditorId
|
|
, &GraphCanvas::AssetEditorNotifications::OnGraphLoaded, graphCanvasGraphId);
|
|
|
|
return AZ::Success(outTabIndex);
|
|
}
|
|
|
|
bool MainWindow::OnFileSave()
|
|
{
|
|
if (auto metaData = m_tabBar->GetTabData(m_activeGraph); metaData && metaData->m_fileState == Tracker::ScriptCanvasFileState::NEW)
|
|
{
|
|
return SaveAssetImpl(m_activeGraph, Save::As);
|
|
}
|
|
else
|
|
{
|
|
return SaveAssetImpl(m_activeGraph, Save::InPlace);
|
|
}
|
|
}
|
|
|
|
bool MainWindow::OnFileSaveAs()
|
|
{
|
|
return SaveAssetImpl(m_activeGraph, Save::As);
|
|
}
|
|
|
|
bool MainWindow::SaveAssetImpl(const ScriptCanvasEditor::SourceHandle& inMemoryAssetId, Save save)
|
|
{
|
|
if (!inMemoryAssetId.IsGraphValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!m_activeGraph.AnyEquals(inMemoryAssetId))
|
|
{
|
|
OnChangeActiveGraphTab(inMemoryAssetId);
|
|
}
|
|
|
|
PrepareAssetForSave(inMemoryAssetId);
|
|
|
|
AZStd::string suggestedFilename;
|
|
AZStd::string suggestedFileFilter;
|
|
bool isValidFileName = false;
|
|
|
|
if (save == Save::InPlace)
|
|
{
|
|
isValidFileName = true;
|
|
suggestedFileFilter = SourceDescription::GetFileExtension();
|
|
suggestedFilename = inMemoryAssetId.Path().c_str();
|
|
}
|
|
else
|
|
{
|
|
suggestedFileFilter = SourceDescription::GetFileExtension();
|
|
|
|
if (inMemoryAssetId.Path().empty())
|
|
{
|
|
suggestedFilename = SourceDescription::GetSuggestedSavePath();
|
|
}
|
|
else
|
|
{
|
|
suggestedFilename = inMemoryAssetId.Path().c_str();
|
|
}
|
|
}
|
|
|
|
EnsureSaveDestinationDirectory(suggestedFilename);
|
|
QString filter = suggestedFileFilter.c_str();
|
|
QString selectedFile = suggestedFilename.c_str();
|
|
|
|
while (!isValidFileName)
|
|
{
|
|
selectedFile = AzQtComponents::FileDialog::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();
|
|
|
|
if (!AZ::StringFunc::EndsWith(filePath, SourceDescription::GetFileExtension(), false))
|
|
{
|
|
filePath += SourceDescription::GetFileExtension();
|
|
}
|
|
|
|
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("@engroot@", assetRootChar.data(), assetRootChar.size());
|
|
assetRoot = assetRootChar.data();
|
|
|
|
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 (!AZ::StringFunc::EndsWith(internalStringFile, SourceDescription::GetFileExtension(), false))
|
|
{
|
|
internalStringFile += SourceDescription::GetFileExtension();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
SaveAs(internalStringFile, inMemoryAssetId);
|
|
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(const VersionExplorer::FileSaveResult& result)
|
|
{
|
|
const bool saveSuccess = result.fileSaveError.empty();
|
|
auto completeDescription = CompleteDescription(m_fileSaver->GetSource());
|
|
auto memoryAsset = completeDescription ? *completeDescription : m_fileSaver->GetSource();
|
|
int saveTabIndex = m_tabBar->FindTab(memoryAsset);
|
|
AZStd::string tabName = saveTabIndex >= 0 ? m_tabBar->tabText(saveTabIndex).toUtf8().data() : "";
|
|
|
|
if (saveSuccess)
|
|
{
|
|
ScriptCanvasEditor::SourceHandle& fileAssetId = memoryAsset;
|
|
int currentTabIndex = m_tabBar->currentIndex();
|
|
|
|
AZ::Data::AssetId oldId = fileAssetId.Id();
|
|
AZ::Data::AssetInfo assetInfo;
|
|
assetInfo.m_assetId = fileAssetId.Id();
|
|
AZ_VerifyWarning("ScriptCanvas", AssetHelpers::GetAssetInfo(fileAssetId.Path().c_str(), assetInfo)
|
|
, "Failed to find asset info for source file just saved: %s", fileAssetId.Path().c_str());
|
|
|
|
const bool assetIdHasChanged = assetInfo.m_assetId.m_guid != fileAssetId.Id();
|
|
fileAssetId = SourceHandle(fileAssetId, assetInfo.m_assetId.m_guid, fileAssetId.Path());
|
|
|
|
// check for saving a graph over another graph with an open tab
|
|
for (;;)
|
|
{
|
|
auto graph = fileAssetId.Get();
|
|
int tabIndexByGraph = m_tabBar->FindTab(graph);
|
|
if (tabIndexByGraph == -1)
|
|
{
|
|
AZ_Warning("ScriptCanvas", false, "unable to find graph just saved");
|
|
break;
|
|
}
|
|
|
|
int saveOverMatch = m_tabBar->FindSaveOverMatch(fileAssetId);
|
|
if (saveOverMatch < 0)
|
|
{
|
|
saveTabIndex = tabIndexByGraph;
|
|
currentTabIndex = m_tabBar->currentIndex();
|
|
break;
|
|
}
|
|
|
|
m_tabBar->CloseTab(saveOverMatch);
|
|
}
|
|
|
|
// this path is questionable, this is a save request that is not the current graph
|
|
// We've saved as over a new graph, so we need to close the old one.
|
|
if (saveTabIndex != currentTabIndex)
|
|
{
|
|
// Invalidate the file asset id so we don't delete trigger the asset flow.
|
|
m_tabBar->setTabData(saveTabIndex, QVariant::fromValue(Widget::GraphTabMetadata()));
|
|
m_tabBar->CloseTab(saveTabIndex);
|
|
saveTabIndex = -1;
|
|
}
|
|
|
|
AzFramework::StringFunc::Path::GetFileName(memoryAsset.Path().c_str(), tabName);
|
|
|
|
if (assetIdHasChanged)
|
|
{
|
|
auto entity = memoryAsset.Get()->GetEntity();
|
|
|
|
auto editorComponents = AZ::EntityUtils::FindDerivedComponents<EditorScriptCanvasComponent>(entity);
|
|
|
|
if (editorComponents.empty())
|
|
{
|
|
if (auto firstRequestBus = EditorScriptCanvasComponentRequestBus::FindFirstHandler(entity->GetId()))
|
|
{
|
|
firstRequestBus->SetAssetId(memoryAsset.Describe());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto editorComponent : editorComponents)
|
|
{
|
|
if (editorComponent->GetAssetId() == oldId)
|
|
{
|
|
editorComponent->SetAssetId(memoryAsset.Describe());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Soft switch the asset id here. We'll do a double scene switch down below to actually switch the active assetid
|
|
m_activeGraph = fileAssetId;
|
|
|
|
if (tabName.at(tabName.size() - 1) == '*' || tabName.at(tabName.size() - 1) == '^')
|
|
{
|
|
tabName = tabName.substr(0, tabName.size() - 2);
|
|
}
|
|
|
|
auto tabData = m_tabBar->GetTabData(saveTabIndex);
|
|
tabData->m_fileState = Tracker::ScriptCanvasFileState::UNMODIFIED;
|
|
tabData->m_assetId = fileAssetId;
|
|
m_tabBar->SetTabData(*tabData, saveTabIndex);
|
|
m_tabBar->SetTabText(saveTabIndex, tabName.c_str());
|
|
}
|
|
else
|
|
{
|
|
const auto failureMessage = AZStd::string::format("Failed to save %s: %s", tabName.c_str(), result.fileSaveError.c_str());
|
|
QMessageBox::critical(this, QString(), QObject::tr(failureMessage.data()));
|
|
}
|
|
|
|
if (m_tabBar->currentIndex() != saveTabIndex)
|
|
{
|
|
m_tabBar->setCurrentIndex(saveTabIndex);
|
|
}
|
|
|
|
UpdateAssignToSelectionState();
|
|
|
|
OnSaveToast toast(tabName, GetActiveGraphCanvasGraphId(), saveSuccess);
|
|
|
|
const bool displayAsNotification = true;
|
|
RunGraphValidation(displayAsNotification);
|
|
|
|
m_closeCurrentGraphAfterSave = false;
|
|
|
|
EnableAssetView(memoryAsset);
|
|
|
|
UpdateSaveState(true);
|
|
UnblockCloseRequests();
|
|
m_fileSaver.reset();
|
|
}
|
|
|
|
bool MainWindow::ActivateAndSaveAsset(const ScriptCanvasEditor::SourceHandle& unsavedAssetId)
|
|
{
|
|
SetActiveAsset(unsavedAssetId);
|
|
return OnFileSave();
|
|
}
|
|
|
|
void MainWindow::SaveAs(AZStd::string_view path, ScriptCanvasEditor::SourceHandle inMemoryAssetId)
|
|
{
|
|
DisableAssetView(inMemoryAssetId);
|
|
UpdateSaveState(false);
|
|
m_fileSaver = AZStd::make_unique<VersionExplorer::FileSaver>
|
|
( nullptr
|
|
, [this](const VersionExplorer::FileSaveResult& fileSaveResult) { OnSaveCallBack(fileSaveResult); });
|
|
|
|
ScriptCanvasEditor::SourceHandle newLocation(inMemoryAssetId, AZ::Uuid::CreateNull(), path);
|
|
MarkRecentSave(newLocation);
|
|
m_fileSaver->Save(newLocation);
|
|
|
|
BlockCloseRequests();
|
|
}
|
|
|
|
void MainWindow::OnFileOpen()
|
|
{
|
|
AZStd::string assetRoot;
|
|
{
|
|
AZStd::array<char, AZ::IO::MaxPathLength> assetRootChar;
|
|
AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", assetRootChar.data(), assetRootChar.size());
|
|
assetRoot = assetRootChar.data();
|
|
}
|
|
|
|
AZStd::string assetPath = AZStd::string::format("%s/scriptcanvas", assetRoot.c_str());
|
|
QString filter;
|
|
|
|
AZStd::set<AZStd::string> filterSet { ".scriptcanvas" };
|
|
|
|
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::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_activeGraph);
|
|
if (fileState != Tracker::ScriptCanvasFileState::INVALID && fileState != Tracker::ScriptCanvasFileState::NEW)
|
|
{
|
|
OnFileSaveCaller();
|
|
}
|
|
}
|
|
}
|
|
|
|
//! GeneralRequestBus
|
|
void MainWindow::OnChangeActiveGraphTab(ScriptCanvasEditor::SourceHandle assetId)
|
|
{
|
|
SetActiveAsset(assetId);
|
|
}
|
|
|
|
AZ::EntityId MainWindow::GetActiveGraphCanvasGraphId() const
|
|
{
|
|
AZ::EntityId graphId{};
|
|
|
|
if (m_activeGraph.IsGraphValid())
|
|
{
|
|
EditorGraphRequestBus::EventResult
|
|
( graphId, m_activeGraph.Get()->GetScriptCanvasId(), &EditorGraphRequests::GetGraphCanvasGraphId);
|
|
}
|
|
|
|
return graphId;
|
|
}
|
|
|
|
ScriptCanvas::ScriptCanvasId MainWindow::GetActiveScriptCanvasId() const
|
|
{
|
|
return FindScriptCanvasIdByAssetId(m_activeGraph);
|
|
}
|
|
|
|
GraphCanvas::GraphId MainWindow::GetGraphCanvasGraphId(const ScriptCanvas::ScriptCanvasId& scriptCanvasId) const
|
|
{
|
|
AZ::EntityId graphId{};
|
|
EditorGraphRequestBus::EventResult(graphId, scriptCanvasId, &EditorGraphRequests::GetGraphCanvasGraphId);
|
|
return graphId;
|
|
}
|
|
|
|
GraphCanvas::GraphId MainWindow::FindGraphCanvasGraphIdByAssetId(const ScriptCanvasEditor::SourceHandle& assetId) const
|
|
{
|
|
AZ::EntityId graphId{};
|
|
|
|
if (assetId.IsGraphValid())
|
|
{
|
|
EditorGraphRequestBus::EventResult
|
|
( graphId, assetId.Get()->GetScriptCanvasId(), &EditorGraphRequests::GetGraphCanvasGraphId);
|
|
}
|
|
|
|
return graphId;
|
|
}
|
|
|
|
ScriptCanvas::ScriptCanvasId MainWindow::FindScriptCanvasIdByAssetId(const ScriptCanvasEditor::SourceHandle& assetId) const
|
|
{
|
|
return assetId.IsGraphValid() ? assetId.Get()->GetScriptCanvasId() : ScriptCanvas::ScriptCanvasId{};
|
|
}
|
|
|
|
ScriptCanvas::ScriptCanvasId MainWindow::GetScriptCanvasId(const GraphCanvas::GraphId& graphCanvasGraphId) const
|
|
{
|
|
return m_tabBar->FindScriptCanvasIdFromGraphCanvasId(graphCanvasGraphId);
|
|
}
|
|
|
|
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 ScriptCanvasEditor::SourceHandle& assetId)
|
|
{
|
|
for (int tabIndex = 0; tabIndex < m_tabBar->count(); ++tabIndex)
|
|
{
|
|
QVariant tabdata = m_tabBar->tabData(tabIndex);
|
|
if (tabdata.isValid())
|
|
{
|
|
auto tabAssetId = tabdata.value<Widget::GraphTabMetadata>();
|
|
if (tabAssetId.m_assetId.AnyEquals(assetId))
|
|
{
|
|
return tabdata;
|
|
}
|
|
}
|
|
}
|
|
return QVariant();
|
|
}
|
|
|
|
bool MainWindow::IsTabOpen(const ScriptCanvasEditor::SourceHandle& fileAssetId, int& outTabIndex) const
|
|
{
|
|
int tabIndex = m_tabBar->FindTab(fileAssetId);
|
|
if (-1 != tabIndex)
|
|
{
|
|
outTabIndex = tabIndex;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MainWindow::ReconnectSceneBuses(ScriptCanvasEditor::SourceHandle previousAsset, ScriptCanvasEditor::SourceHandle nextAsset)
|
|
{
|
|
// Disconnect previous asset
|
|
AZ::EntityId previousScriptCanvasSceneId;
|
|
if (previousAsset.IsGraphValid())
|
|
{
|
|
previousScriptCanvasSceneId = previousAsset.Get()->GetScriptCanvasId();
|
|
GraphCanvas::SceneNotificationBus::MultiHandler::BusDisconnect(previousScriptCanvasSceneId);
|
|
}
|
|
|
|
AZ::EntityId nextAssetGraphCanvasId;
|
|
if (nextAsset.IsGraphValid())
|
|
{
|
|
// Connect the next asset
|
|
EditorGraphRequestBus::EventResult(nextAssetGraphCanvasId, nextAsset.Get()->GetScriptCanvasId(), &EditorGraphRequests::GetGraphCanvasGraphId);
|
|
|
|
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 ScriptCanvasEditor::SourceHandle& fileAssetId)
|
|
{
|
|
if (m_activeGraph.AnyEquals(fileAssetId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
AssetHelpers::PrintInfo("SetActiveAsset : from: %s to %s", m_activeGraph.ToString().c_str(), fileAssetId.ToString().c_str());
|
|
|
|
if (fileAssetId.IsGraphValid())
|
|
{
|
|
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_activeGraph.IsGraphValid())
|
|
{
|
|
// 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
|
|
auto oldTab = m_tabBar->FindTab(m_activeGraph);
|
|
if (auto view = m_tabBar->ModTabView(oldTab))
|
|
{
|
|
view->hide();
|
|
m_layout->removeWidget(view);
|
|
m_tabBar->ClearTabView(oldTab);
|
|
}
|
|
}
|
|
|
|
if (fileAssetId.IsGraphValid())
|
|
{
|
|
ScriptCanvasEditor::SourceHandle previousAssetId = m_activeGraph;
|
|
m_activeGraph = fileAssetId;
|
|
RefreshActiveAsset();
|
|
ReconnectSceneBuses(previousAssetId, m_activeGraph);
|
|
}
|
|
else
|
|
{
|
|
ScriptCanvasEditor::SourceHandle previousAssetId = m_activeGraph;
|
|
m_activeGraph.Clear();
|
|
m_emptyCanvas->show();
|
|
ReconnectSceneBuses(previousAssetId, m_activeGraph);
|
|
SignalActiveSceneChanged(ScriptCanvasEditor::SourceHandle());
|
|
}
|
|
|
|
UpdateUndoCache(fileAssetId);
|
|
RefreshSelection();
|
|
}
|
|
|
|
void MainWindow::RefreshActiveAsset()
|
|
{
|
|
if (m_activeGraph.IsGraphValid())
|
|
{
|
|
AssetHelpers::PrintInfo("RefreshActiveAsset : m_activeGraph (%s)", m_activeGraph.ToString().c_str());
|
|
if (auto view = m_tabBar->ModOrCreateTabView(m_tabBar->FindTab(m_activeGraph)))
|
|
{
|
|
view->ShowScene(m_activeGraph.Get()->GetScriptCanvasId());
|
|
m_layout->addWidget(view);
|
|
view->show();
|
|
m_emptyCanvas->hide();
|
|
SignalActiveSceneChanged(m_activeGraph);
|
|
}
|
|
else
|
|
{
|
|
SetActiveAsset({});
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::Clear()
|
|
{
|
|
m_tabBar->CloseAllTabs();
|
|
SetActiveAsset({});
|
|
}
|
|
|
|
void MainWindow::OnTabCloseButtonPressed(int index)
|
|
{
|
|
QVariant tabdata = m_tabBar->tabData(index);
|
|
if (tabdata.isValid())
|
|
{
|
|
Widget::GraphTabMetadata tabMetadata = tabdata.value<Widget::GraphTabMetadata>();
|
|
Tracker::ScriptCanvasFileState fileState = tabMetadata.m_fileState;
|
|
UnsavedChangesOptions saveDialogResults = UnsavedChangesOptions::CONTINUE_WITHOUT_SAVING;
|
|
|
|
if (fileState == Tracker::ScriptCanvasFileState::NEW
|
|
|| fileState == Tracker::ScriptCanvasFileState::MODIFIED
|
|
|| fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED)
|
|
{
|
|
SetActiveAsset(tabMetadata.m_assetId);
|
|
saveDialogResults = ShowSaveDialog(m_tabBar->tabText(index).toUtf8().constData());
|
|
}
|
|
|
|
if (saveDialogResults == UnsavedChangesOptions::SAVE)
|
|
{
|
|
m_closeCurrentGraphAfterSave = true;
|
|
SaveAssetImpl(tabMetadata.m_assetId, fileState == Tracker::ScriptCanvasFileState::NEW ? Save::As : Save::InPlace);
|
|
}
|
|
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<Widget::GraphTabMetadata>();
|
|
SaveAssetImpl(assetId.m_assetId, Save::InPlace);
|
|
}
|
|
}
|
|
|
|
void MainWindow::CloseAllTabs()
|
|
{
|
|
m_isClosingTabs = true;
|
|
m_skipTabOnClose.Clear();
|
|
|
|
CloseNextTab();
|
|
}
|
|
|
|
void MainWindow::CloseAllTabsBut(int index)
|
|
{
|
|
QVariant tabdata = m_tabBar->tabData(index);
|
|
if (tabdata.isValid())
|
|
{
|
|
auto assetId = tabdata.value<Widget::GraphTabMetadata>().m_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<Widget::GraphTabMetadata>();
|
|
if (!assetId.m_assetId.Path().empty())
|
|
{
|
|
clipBoard->setText(assetId.m_assetId.Path().c_str());
|
|
}
|
|
else
|
|
{
|
|
clipBoard->setText(m_tabBar->tabText(index));
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::OnActiveFileStateChanged()
|
|
{
|
|
UpdateAssignToSelectionState();
|
|
}
|
|
|
|
void MainWindow::CloseNextTab()
|
|
{
|
|
if (m_isClosingTabs)
|
|
{
|
|
if (m_tabBar->count() == 0
|
|
|| (m_tabBar->count() == 1 && m_skipTabOnClose.IsGraphValid()))
|
|
{
|
|
m_isClosingTabs = false;
|
|
m_skipTabOnClose.Clear();
|
|
return;
|
|
}
|
|
|
|
int tab = 0;
|
|
|
|
while (tab < m_tabBar->count())
|
|
{
|
|
QVariant tabdata = m_tabBar->tabData(tab);
|
|
if (tabdata.isValid())
|
|
{
|
|
auto assetId = tabdata.value<Widget::GraphTabMetadata>();
|
|
|
|
if (!assetId.m_assetId.AnyEquals(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<Widget::GraphTabMetadata>();
|
|
|
|
|
|
if (tabAssetId.m_canvasWidget)
|
|
{
|
|
tabAssetId.m_canvasWidget->hide();
|
|
}
|
|
|
|
bool activeSet = false;
|
|
|
|
if (tabAssetId.m_assetId.AnyEquals(m_activeGraph))
|
|
{
|
|
SetActiveAsset({});
|
|
activeSet = true;
|
|
}
|
|
|
|
m_tabBar->CloseTab(index);
|
|
m_tabBar->update();
|
|
RemoveScriptCanvasAsset(tabAssetId.m_assetId);
|
|
|
|
if (!activeSet && 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();
|
|
}
|
|
|
|
if (m_minimap)
|
|
{
|
|
addDockWidget(Qt::LeftDockWidgetArea, m_minimap);
|
|
m_minimap->setFloating(false);
|
|
m_minimap->show();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
if (m_minimap)
|
|
{
|
|
addDockWidget(Qt::RightDockWidgetArea, m_minimap);
|
|
m_minimap->setFloating(false);
|
|
m_minimap->show();
|
|
}
|
|
|
|
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_activeGraph.IsGraphValid())
|
|
{
|
|
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()
|
|
{
|
|
using namespace VersionExplorer;
|
|
auto versionExplorer = aznew VersionExplorer::Controller(this);
|
|
versionExplorer->exec();
|
|
|
|
const ModificationResults* result = nullptr;
|
|
ModelRequestsBus::BroadcastResult(result, &ModelRequestsTraits::GetResults);
|
|
if (result && !result->m_failures.empty())
|
|
{
|
|
// If there are graphs that need manual correction, show the helper
|
|
UpgradeHelper* upgradeHelper = new UpgradeHelper(this);
|
|
upgradeHelper->show();
|
|
}
|
|
|
|
delete versionExplorer;
|
|
}
|
|
|
|
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);
|
|
m_takeScreenshot->setEnabled(enabled);
|
|
|
|
// File Menu
|
|
ui->action_Close->setEnabled(enabled);
|
|
|
|
RefreshGraphPreferencesAction();
|
|
|
|
UpdateAssignToSelectionState();
|
|
UpdateUndoRedoState();
|
|
}
|
|
|
|
void MainWindow::OnWorkspaceRestoreStart()
|
|
{
|
|
m_isRestoringWorkspace = true;
|
|
}
|
|
|
|
void MainWindow::OnWorkspaceRestoreEnd(ScriptCanvasEditor::SourceHandle lastFocusAsset)
|
|
{
|
|
if (m_isRestoringWorkspace)
|
|
{
|
|
m_isRestoringWorkspace = false;
|
|
|
|
if (m_queuedFocusOverride.IsGraphValid())
|
|
{
|
|
SetActiveAsset(m_queuedFocusOverride);
|
|
m_queuedFocusOverride.Clear();
|
|
}
|
|
else if (lastFocusAsset.IsGraphValid())
|
|
{
|
|
SetActiveAsset(lastFocusAsset);
|
|
}
|
|
|
|
if (!m_activeGraph.IsGraphValid())
|
|
{
|
|
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_activeGraph.IsGraphValid();
|
|
|
|
if (buttonEnabled)
|
|
{
|
|
const Tracker::ScriptCanvasFileState& fileState = GetAssetFileState(m_activeGraph);
|
|
if (fileState == Tracker::ScriptCanvasFileState::INVALID || fileState == Tracker::ScriptCanvasFileState::NEW || fileState == Tracker::ScriptCanvasFileState::SOURCE_REMOVED)
|
|
{
|
|
buttonEnabled = false;
|
|
}
|
|
|
|
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)
|
|
{
|
|
ui->action_Save->setEnabled(enabled);
|
|
ui->action_Save_As->setEnabled(enabled);
|
|
}
|
|
|
|
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([[maybe_unused]] const ScriptCanvasEditor::SourceHandle& assetId
|
|
, [[maybe_unused]] AZ::EntityId assetNodeId) const
|
|
{
|
|
AZ::EntityId editorEntityId{};
|
|
// AssetTrackerRequestBus::BroadcastResult
|
|
// ( editorEntityId, &AssetTrackerRequests::GetEditorEntityIdFromSceneEntityId, assetId.Id(), assetNodeId);
|
|
// #sc_editor_asset_redux fix logger
|
|
return editorEntityId;
|
|
}
|
|
|
|
AZ::EntityId MainWindow::FindAssetNodeIdByEditorNodeId([[maybe_unused]] const ScriptCanvasEditor::SourceHandle& assetId
|
|
, [[maybe_unused]] AZ::EntityId editorNodeId) const
|
|
{
|
|
AZ::EntityId sceneEntityId{};
|
|
// AssetTrackerRequestBus::BroadcastResult
|
|
// ( sceneEntityId, &AssetTrackerRequests::GetSceneEntityIdFromEditorEntityId, assetId.Id(), editorNodeId);
|
|
// #sc_editor_asset_redux fix logger
|
|
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_activeGraph.IsGraphValid())
|
|
{
|
|
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));
|
|
|
|
auto setSlotTypeAction = aznew SetDataSlotTypeMenuAction(&contextMenu);
|
|
// Changing slot type is disabled temporarily because now that that user data slots are correctly coordinated with their reference
|
|
// variables, their type cannot be changed. The next change will allow all variables to change their type post creation, and then
|
|
// that will allow this action to be enabled.
|
|
setSlotTypeAction->setEnabled(false);
|
|
contextMenu.AddMenuAction(setSlotTypeAction);
|
|
|
|
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::CloseCurrentGraph))
|
|
{
|
|
RemoveSystemTickAction(SystemTickActionFlag::CloseCurrentGraph);
|
|
|
|
if (m_tabBar)
|
|
{
|
|
m_tabBar->tabCloseRequested(m_tabBar->currentIndex());
|
|
}
|
|
}
|
|
|
|
if (HasSystemTickAction(SystemTickActionFlag::CloseNextTabAction))
|
|
{
|
|
RemoveSystemTickAction(SystemTickActionFlag::CloseNextTabAction);
|
|
CloseNextTab();
|
|
}
|
|
|
|
ClearStaleSaves();
|
|
}
|
|
|
|
void MainWindow::OnCommandStarted(AZ::Crc32)
|
|
{
|
|
PushPreventUndoStateUpdate();
|
|
}
|
|
|
|
void MainWindow::OnCommandFinished(AZ::Crc32)
|
|
{
|
|
PopPreventUndoStateUpdate();
|
|
}
|
|
|
|
void MainWindow::PrepareActiveAssetForSave()
|
|
{
|
|
PrepareAssetForSave(m_activeGraph);
|
|
}
|
|
|
|
void MainWindow::PrepareAssetForSave(const ScriptCanvasEditor::SourceHandle& /*assetId*/)
|
|
{
|
|
}
|
|
|
|
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 = GetAssetFileState(m_activeGraph);;
|
|
|
|
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_activeGraph);
|
|
|
|
if (fileState == Tracker::ScriptCanvasFileState::MODIFIED
|
|
|| fileState == Tracker::ScriptCanvasFileState::UNMODIFIED)
|
|
{
|
|
AssignGraphToEntityImpl(entityId);
|
|
}
|
|
}
|
|
|
|
ScriptCanvasEditor::Tracker::ScriptCanvasFileState MainWindow::GetAssetFileState(ScriptCanvasEditor::SourceHandle assetId) const
|
|
{
|
|
auto dataOptional = m_tabBar->GetTabData(assetId);
|
|
return dataOptional ? dataOptional->m_fileState : Tracker::ScriptCanvasFileState::INVALID;
|
|
}
|
|
|
|
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)
|
|
{
|
|
usableRequestBus->SetAssetId(m_activeGraph.Describe());
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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());
|
|
OpenNextFile();
|
|
}
|
|
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(const ScriptCanvasEditor::SourceHandle& memoryAssetId)
|
|
{
|
|
if (auto view = m_tabBar->ModTabView(m_tabBar->FindTab(memoryAssetId)))
|
|
{
|
|
view->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(const ScriptCanvasEditor::SourceHandle& memoryAssetId)
|
|
{
|
|
if (auto view = m_tabBar->ModTabView(m_tabBar->FindTab(memoryAssetId)))
|
|
{
|
|
view->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();
|
|
}
|
|
|
|
|
|
void MainWindow::ClearStaleSaves()
|
|
{
|
|
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
|
|
auto timeNow = AZStd::chrono::system_clock::now();
|
|
AZStd::erase_if(m_saves, [&timeNow](const auto& item)
|
|
{
|
|
AZStd::sys_time_t delta = AZStd::chrono::seconds(timeNow - item.second).count();
|
|
return delta > 2.0f;
|
|
});
|
|
}
|
|
|
|
bool MainWindow::IsRecentSave(const SourceHandle& handle) const
|
|
{
|
|
AZStd::lock_guard<AZStd::recursive_mutex> lock(const_cast<MainWindow*>(this)->m_mutex);
|
|
AZStd::string key = handle.Path().Native();
|
|
AZStd::to_lower(key.begin(), key.end());
|
|
auto iter = m_saves.find(key);
|
|
return iter != m_saves.end();
|
|
}
|
|
|
|
void MainWindow::MarkRecentSave(const SourceHandle& handle)
|
|
{
|
|
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
|
|
AZStd::string key = handle.Path().Native();
|
|
AZStd::to_lower(key.begin(), key.end());
|
|
m_saves[key] = AZStd::chrono::system_clock::now();
|
|
}
|
|
|
|
#include <Editor/View/Windows/moc_MainWindow.cpp>
|
|
}
|