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