/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ScriptCanvasEditor { //////////////////////////////// // VariablePropertiesComponent //////////////////////////////// void VariablePropertiesComponent::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serializeContext = azrtti_cast(context); if (serializeContext) { serializeContext->Class() ->Version(1) ->Field("VariableName", &VariablePropertiesComponent::m_variableName) ->Field("VariableDatum", &VariablePropertiesComponent::m_variable) ; AZ::EditContext* editContext = serializeContext->GetEditContext(); if (editContext) { editContext->Class("Variable Properties", "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "Properties") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &VariablePropertiesComponent::GetTitle) ->DataElement(AZ::Edit::UIHandlers::Default, &VariablePropertiesComponent::m_variableName, "Name", "") ->Attribute(AZ::Edit::Attributes::StringLineEditingCompleteNotify, &VariablePropertiesComponent::OnNameChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &VariablePropertiesComponent::m_variable, "Datum", "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ; } } } AZ::Entity* VariablePropertiesComponent::CreateVariablePropertiesEntity() { AZ::Entity* entity = aznew AZ::Entity("VariablePropertiesHelper"); entity->CreateComponent(); return entity; } VariablePropertiesComponent::VariablePropertiesComponent() : m_variableName(AZStd::string()) , m_variable(nullptr) , m_componentTitle("Variable") { } const char* VariablePropertiesComponent::GetTitle() { return m_componentTitle.c_str(); } void VariablePropertiesComponent::SetVariable(ScriptCanvas::GraphVariable* variable) { if (variable) { ScriptCanvas::VariableNotificationBus::Handler::BusDisconnect(); m_variable = variable; m_componentTitle.clear(); m_variableName.clear(); m_variableName = m_variable->GetVariableName(); const AZStd::string variableTypeName = TranslationHelper::GetSafeTypeName(m_variable->GetDatum()->GetType()); m_variable->SetDisplayName(variableTypeName); m_componentTitle = AZStd::string::format("%s Variable", variableTypeName.data()); ScriptCanvas::VariableNotificationBus::Handler::BusConnect(m_variable->GetGraphScopedId()); m_scriptCanvasGraphId = m_variable->GetGraphScopedId().m_scriptCanvasId; } } void VariablePropertiesComponent::OnNameChanged() { if (m_variable == nullptr) { return; } ScriptCanvas::VariableNotificationBus::Handler::BusDisconnect(); AZ::Outcome outcome = AZ::Failure(AZStd::string()); AZStd::string_view oldVariableName = m_variable->GetVariableName(); if (oldVariableName != m_variableName) { ScriptCanvas::VariableRequestBus::EventResult(outcome, m_variable->GetGraphScopedId(), &ScriptCanvas::VariableRequests::RenameVariable, m_variableName); AZ_Warning("VariablePropertiesComponent", outcome.IsSuccess(), "Could not rename variable: %s", outcome.GetError().c_str()); if (!outcome.IsSuccess()) { // Revert the variable name if we couldn't rename it (e.g. not unique) m_variableName = oldVariableName; PropertyGridRequestBus::Broadcast(&PropertyGridRequests::RefreshPropertyGrid); } else { GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_scriptCanvasGraphId); } } ScriptCanvas::VariableNotificationBus::Handler::BusConnect(m_variable->GetGraphScopedId()); } void VariablePropertiesComponent::OnVariableRemoved() { ScriptCanvas::VariableNotificationBus::Handler::BusDisconnect(); m_variableName = AZStd::string(); m_variable = nullptr; } void VariablePropertiesComponent::OnVariableValueChanged() { GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_scriptCanvasGraphId); PropertyGridRequestBus::Broadcast(&PropertyGridRequests::RefreshPropertyGrid); } void VariablePropertiesComponent::OnVariableRenamed(AZStd::string_view variableName) { m_variableName = variableName; PropertyGridRequestBus::Broadcast(&PropertyGridRequests::RefreshPropertyGrid); } void VariablePropertiesComponent::OnVariableScopeChanged() { GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_scriptCanvasGraphId); PropertyGridRequestBus::Broadcast(&PropertyGridRequests::RefreshPropertyGrid); } ///////////////////////////// // VariablePanelContextMenu ///////////////////////////// VariablePanelContextMenu::VariablePanelContextMenu(VariableDockWidget* dockWidget, const ScriptCanvas::ScriptCanvasId& scriptCanvasId, ScriptCanvas::VariableId varId) : QMenu() { AZ::EntityId graphCanvasGraphId; GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::GetGraphCanvasGraphId, scriptCanvasId); AZStd::string variableName; ScriptCanvas::GraphVariableManagerRequestBus::EventResult(variableName, scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::GetVariableName, varId); QAction* getAction = new QAction(QObject::tr("Get %1").arg(variableName.c_str()), this); getAction->setToolTip(QObject::tr("Adds a Get %1 variable node onto the active graph.").arg(variableName.c_str())); getAction->setStatusTip(QObject::tr("Adds a Get %1 variable node onto the active graph.").arg(variableName.c_str())); QObject::connect(getAction, &QAction::triggered, [graphCanvasGraphId, varId](bool) { CreateGetVariableNodeMimeEvent mimeEvent(varId); AZ::EntityId viewId; GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId); AZ::Vector2 viewCenter; GraphCanvas::ViewRequestBus::EventResult(viewCenter, viewId, &GraphCanvas::ViewRequests::GetViewSceneCenter); mimeEvent.ExecuteEvent(viewCenter, viewCenter, graphCanvasGraphId); }); QAction* setAction = new QAction(QObject::tr("Set %1").arg(variableName.c_str()), this); setAction->setToolTip(QObject::tr("Adds a Set %1 variable node onto the active graph.").arg(variableName.c_str())); setAction->setStatusTip(QObject::tr("Adds a Set %1 variable node onto the active graph.").arg(variableName.c_str())); QObject::connect(setAction, &QAction::triggered, [graphCanvasGraphId, varId](bool) { CreateSetVariableNodeMimeEvent mimeEvent(varId); AZ::EntityId viewId; GraphCanvas::SceneRequestBus::EventResult(viewId, graphCanvasGraphId, &GraphCanvas::SceneRequests::GetViewId); AZ::Vector2 viewCenter; GraphCanvas::ViewRequestBus::EventResult(viewCenter, viewId, &GraphCanvas::ViewRequests::GetViewSceneCenter); mimeEvent.ExecuteEvent(viewCenter, viewCenter, graphCanvasGraphId); }); QAction* copyAction = new QAction(QObject::tr("Copy %1").arg(variableName.c_str()), this); copyAction->setToolTip(QObject::tr("Copies the variable called - %1").arg(variableName.c_str())); copyAction->setStatusTip(QObject::tr("Copies the variable called - %1").arg(variableName.c_str())); QObject::connect(copyAction, &QAction::triggered, [dockWidget, varId](bool) { GraphVariablesTableView::CopyVariableToClipboard(dockWidget->GetActiveScriptCanvasId(), varId); }); QAction* pasteAction = new QAction(QObject::tr("Paste").arg(variableName.c_str()), this); pasteAction->setToolTip(QObject::tr("Pastes the variable currently on the clipboard").arg(variableName.c_str())); pasteAction->setStatusTip(QObject::tr("Pastes the variable currently on the clipboard").arg(variableName.c_str())); pasteAction->setEnabled(GraphVariablesTableView::HasCopyVariableData()); QObject::connect(pasteAction, &QAction::triggered, [dockWidget, varId](bool) { GraphVariablesTableView::HandleVariablePaste(dockWidget->GetActiveScriptCanvasId()); }); QAction* duplicateAction = new QAction(QObject::tr("Duplicate %1").arg(variableName.c_str()), this); duplicateAction->setToolTip(QObject::tr("Duplicates the variable called - %1").arg(variableName.c_str())); duplicateAction->setStatusTip(QObject::tr("Duplicates the variable called - %1").arg(variableName.c_str())); QObject::connect(duplicateAction, &QAction::triggered, [dockWidget, varId](bool) { dockWidget->OnDuplicateVariable(varId); }); QAction* deleteAction = new QAction(QObject::tr("Delete %1").arg(variableName.c_str()), this); deleteAction->setToolTip(QObject::tr("Deletes the variable called - %1").arg(variableName.c_str())); deleteAction->setStatusTip(QObject::tr("Deletes the variable called - %1").arg(variableName.c_str())); QObject::connect(deleteAction, &QAction::triggered, [dockWidget, varId](bool) { AZStd::unordered_set< ScriptCanvas::VariableId > variableIds = { varId }; dockWidget->OnDeleteVariables(variableIds); }); addAction(getAction); addAction(setAction); addSeparator(); addAction(copyAction); addAction(pasteAction); addAction(duplicateAction); addAction(deleteAction); addSeparator(); // Setup exposure options ScriptCanvas::GraphScopedVariableId variableId; variableId.m_scriptCanvasId = scriptCanvasId; variableId.m_identifier = varId; ScriptCanvas::GraphVariable* variable = nullptr; ScriptCanvas::VariableRequestBus::EventResult(variable, variableId, &ScriptCanvas::VariableRequests::GetVariable); AZ_Assert(variable, "The variable must exist at this point"); AZ::Data::AssetType assetType; AssetTrackerRequestBus::BroadcastResult(assetType, &AssetTrackerRequests::GetAssetType, scriptCanvasId); // Exposing variables is only available for Function assets QMenu* scopeSubMenu = new QMenu(QObject::tr("Scope"), this); QActionGroup* actionGroup = new QActionGroup(scopeSubMenu); actionGroup->setExclusive(true); AZStd::vector scopes = { ScriptCanvas::VariableFlags::Scope::Local, ScriptCanvas::VariableFlags::Scope::Input }; if (assetType == azrtti_typeid()) { scopes.emplace_back(ScriptCanvas::VariableFlags::Scope::Output); scopes.emplace_back(ScriptCanvas::VariableFlags::Scope::InOut); } for (auto scopeType : scopes) { QAction* exposureAction = new QAction(QObject::tr(ScriptCanvas::VariableFlags::GetScopeDisplayLabel(scopeType)), this); exposureAction->setCheckable(true); exposureAction->setChecked(variable->GetScope() == scopeType); exposureAction->setActionGroup(actionGroup); exposureAction->setToolTip(QObject::tr(ScriptCanvas::VariableFlags::GetScopeToolTip(scopeType))); QObject::connect(exposureAction, &QAction::triggered, this, [variableId, scopeType](bool) { ScriptCanvas::GraphVariable* variable = nullptr; ScriptCanvas::VariableRequestBus::EventResult(variable, variableId, &ScriptCanvas::VariableRequests::GetVariable); if (variable) { variable->SetScope(scopeType); } }); scopeSubMenu->addAction(exposureAction); } addMenu(scopeSubMenu); } /////////////////////// // VariableDockWidget /////////////////////// AZStd::string VariableDockWidget::ConstructDefaultVariableName(AZ::u32 variableCounter) { return AZStd::string::format("Variable %u", variableCounter); } AZStd::string VariableDockWidget::FindDefaultVariableName(const ScriptCanvas::ScriptCanvasId& scriptCanvasExecutionId) { ScriptCanvas::VariableValidationOutcome nameAvailable = AZ::Failure(ScriptCanvas::GraphVariableValidationErrorCode::Unknown); AZStd::string varName; do { AZ::u32 varCounter = 0; SceneCounterRequestBus::EventResult(varCounter, scriptCanvasExecutionId, &SceneCounterRequests::GetNewVariableCounter); varName = ConstructDefaultVariableName(varCounter); ScriptCanvas::GraphVariableManagerRequestBus::EventResult(nameAvailable, scriptCanvasExecutionId, &ScriptCanvas::GraphVariableManagerRequests::IsNameValid, varName); } while (!nameAvailable); return varName; } VariableDockWidget::VariableDockWidget(QWidget* parent /*= nullptr*/) : AzQtComponents::StyledDockWidget(parent) , m_manipulatingSelection(false) , ui(new Ui::VariableDockWidget()) { ui->setupUi(this); ui->graphVariables->setContentsMargins(0, 0, 0, 0); ui->graphVariables->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(ui->graphVariables, &GraphVariablesTableView::SelectionChanged, this, &VariableDockWidget::OnSelectionChanged); QObject::connect(ui->graphVariables, &QWidget::customContextMenuRequested, this, &VariableDockWidget::OnContextMenuRequested); ui->searchFilter->setClearButtonEnabled(true); QObject::connect(ui->searchFilter, &QLineEdit::textChanged, this, &VariableDockWidget::OnQuickFilterChanged); QObject::connect(ui->searchFilter, &QLineEdit::returnPressed, this, &VariableDockWidget::OnReturnPressed); // Tell the widget to auto create our context menu, for now setContextMenuPolicy(Qt::ActionsContextMenu); // Add button is disabled by default, since we don't want to switch panels until we have an active scene. connect(ui->addButton, &QPushButton::clicked, this, &VariableDockWidget::OnAddVariableButton); ui->addButton->setEnabled(false); ui->searchFilter->setEnabled(false); QObject::connect(ui->variablePalette, &VariablePaletteTableView::CreateVariable, this, &VariableDockWidget::OnCreateVariable); QObject::connect(ui->variablePalette, &VariablePaletteTableView::CreateNamedVariable, this, &VariableDockWidget::OnCreateNamedVariable); QObject::connect(ui->graphVariables, &GraphVariablesTableView::DeleteVariables, this, &VariableDockWidget::OnDeleteVariables); m_filterTimer.setInterval(250); m_filterTimer.setSingleShot(true); m_filterTimer.stop(); QObject::connect(&m_filterTimer, &QTimer::timeout, this, &VariableDockWidget::UpdateFilter); GraphCanvas::AssetEditorNotificationBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId); ShowGraphVariables(); VariableAutomationRequestBus::Handler::BusConnect(); } VariableDockWidget::~VariableDockWidget() { GraphCanvas::AssetEditorNotificationBus::Handler::BusDisconnect(); VariableAutomationRequestBus::Handler::BusDisconnect(); } void VariableDockWidget::PopulateVariablePalette(const AZStd::unordered_set< AZ::Uuid >& objectTypes) { ui->variablePalette->PopulateVariablePalette(objectTypes); } void VariableDockWidget::OnActiveGraphChanged(const GraphCanvas::GraphId& graphCanvasGraphId) { ClearFilter(); m_graphCanvasGraphId = graphCanvasGraphId; m_scriptCanvasId.SetInvalid(); GeneralRequestBus::BroadcastResult(m_scriptCanvasId, &GeneralRequests::GetScriptCanvasId, graphCanvasGraphId); ui->graphVariables->SetActiveScene(m_scriptCanvasId); ui->variablePalette->SetActiveScene(m_scriptCanvasId); ui->addButton->setEnabled(m_scriptCanvasId.IsValid()); ui->searchFilter->setEnabled(m_scriptCanvasId.IsValid()); ShowGraphVariables(); } AZStd::vector< ScriptCanvas::Data::Type > VariableDockWidget::GetPrimitiveTypes() const { AZStd::vector< ScriptCanvas::Data::Type > primitiveTypes; auto variableTypes = ui->variablePalette->GetVariableTypePaletteModel()->GetVariableTypes(); for (auto variableType : variableTypes) { ScriptCanvas::Data::Type dataType = ScriptCanvas::Data::FromAZType(variableType); if (ScriptCanvas::Data::IsValueType(dataType)) { primitiveTypes.push_back(dataType); } } return primitiveTypes; } AZStd::vector< ScriptCanvas::Data::Type > VariableDockWidget::GetBehaviorContextObjectTypes() const { AZStd::vector< ScriptCanvas::Data::Type > bcoTypes; auto variableTypes = ui->variablePalette->GetVariableTypePaletteModel()->GetVariableTypes(); for (auto variableType : variableTypes) { ScriptCanvas::Data::Type dataType = ScriptCanvas::Data::FromAZType(variableType); if (!ScriptCanvas::Data::IsValueType(dataType)) { if (!ScriptCanvas::Data::IsContainerType(dataType)) { bcoTypes.emplace_back(dataType); } } } return bcoTypes; } AZStd::vector< ScriptCanvas::Data::Type > VariableDockWidget::GetMapTypes() const { AZStd::vector< ScriptCanvas::Data::Type > variableDataTypes; auto mapTypes = ui->variablePalette->GetMapTypes(); for (auto mapType : mapTypes) { ScriptCanvas::Data::Type dataType = ScriptCanvas::Data::FromAZType(mapType); variableDataTypes.emplace_back(dataType); } return variableDataTypes; } AZStd::vector< ScriptCanvas::Data::Type > VariableDockWidget::GetArrayTypes() const { AZStd::vector< ScriptCanvas::Data::Type > variableDataTypes; auto arrayTypes = ui->variablePalette->GetArrayTypes(); for (auto arrayType : arrayTypes) { ScriptCanvas::Data::Type dataType = ScriptCanvas::Data::FromAZType(arrayType); variableDataTypes.emplace_back(dataType); } return variableDataTypes; } void VariableDockWidget::OnEscape() { ShowGraphVariables(); } void VariableDockWidget::focusOutEvent(QFocusEvent* focusEvent) { AzQtComponents::StyledDockWidget::focusOutEvent(focusEvent); if (ui->stackedWidget->currentIndex() == ui->stackedWidget->indexOf(ui->variablePalettePage)) { ShowGraphVariables(); } } const ScriptCanvas::ScriptCanvasId& VariableDockWidget::GetActiveScriptCanvasId() const { return m_scriptCanvasId; } bool VariableDockWidget::IsValidVariableType(const ScriptCanvas::Data::Type& dataType) const { bool isValid = false; AZ::Uuid azType = ScriptCanvas::Data::ToAZType(dataType); if (ScriptCanvas::Data::IsMapContainerType(dataType)) { auto mapTypes = ui->variablePalette->GetMapTypes(); auto findIter = AZStd::find(mapTypes.begin(), mapTypes.end(), azType); isValid = findIter != mapTypes.end(); } else if (ScriptCanvas::Data::IsVectorContainerType(dataType)) { auto arrayTypes = ui->variablePalette->GetArrayTypes(); auto findIter = AZStd::find(arrayTypes.begin(), arrayTypes.end(), azType); isValid = findIter != arrayTypes.end(); } else { const auto& variableTypes = ui->variablePalette->GetVariableTypePaletteModel()->GetVariableTypes(); auto findIter = AZStd::find(variableTypes.begin(), variableTypes.end(), azType); isValid = findIter != variableTypes.end(); } return isValid; } bool VariableDockWidget::IsShowingVariablePalette() const { return ui->stackedWidget->currentIndex() == ui->stackedWidget->indexOf(ui->variablePalettePage); } void VariableDockWidget::ShowVariablePalette() { ui->stackedWidget->setCurrentIndex(ui->stackedWidget->indexOf(ui->variablePalettePage)); ClearFilter(); ui->searchFilter->setPlaceholderText("Variable Type..."); FocusOnSearchFilter(); ui->searchFilter->setCompleter(ui->variablePalette->GetVariableCompleter()); AzToolsFramework::EditorEvents::Bus::Handler::BusConnect(); } bool VariableDockWidget::IsShowingGraphVariables() const { return ui->stackedWidget->currentIndex() == ui->stackedWidget->indexOf(ui->graphVariablesPage); } void VariableDockWidget::ShowGraphVariables() { ui->stackedWidget->setCurrentIndex(ui->stackedWidget->indexOf(ui->graphVariablesPage)); ClearFilter(); ui->variablePalette->clearSelection(); ui->searchFilter->setPlaceholderText("Search..."); ui->searchFilter->setCompleter(nullptr); AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect(); } void VariableDockWidget::FocusOnSearchFilter() { ui->searchFilter->setFocus(Qt::FocusReason::MouseFocusReason); } void VariableDockWidget::ClearFilter() { { QSignalBlocker blocker(ui->searchFilter); ui->searchFilter->setText(""); } UpdateFilter(); } void VariableDockWidget::UpdateFilter() { if (IsShowingGraphVariables()) { ui->graphVariables->SetFilter(ui->searchFilter->text()); } else if (IsShowingVariablePalette()) { ui->variablePalette->SetFilter(ui->searchFilter->userInputText()); } } void VariableDockWidget::OnReturnPressed() { if (IsShowingVariablePalette()) { ui->variablePalette->TryCreateVariableByTypeName(ui->searchFilter->text().toStdString().c_str()); } else if (IsShowingGraphVariables()) { UpdateFilter(); } } void VariableDockWidget::OnQuickFilterChanged(const QString& text) { if(text.isEmpty()) { //If field was cleared, update immediately UpdateFilter(); return; } m_filterTimer.stop(); m_filterTimer.start(); } void VariableDockWidget::RefreshModel() { ui->graphVariables->SetActiveScene(m_scriptCanvasId); } void VariableDockWidget::OnAddVariableButton() { int index = ui->stackedWidget->currentIndex(); // Switch between pages if (index == ui->stackedWidget->indexOf(ui->graphVariablesPage)) { ShowVariablePalette(); } else if(index == ui->stackedWidget->indexOf(ui->variablePalettePage)) { ShowGraphVariables(); } } void VariableDockWidget::OnContextMenuRequested(const QPoint& pos) { AzToolsFramework::EditorPickModeRequestBus::Broadcast(&AzToolsFramework::EditorPickModeRequests::StopEntityPickMode); QModelIndex index = ui->graphVariables->indexAt(pos); QActionGroup actionGroup(this); actionGroup.setExclusive(true); QAction* sortByName = actionGroup.addAction("Sort by name"); sortByName->setCheckable(true); QAction* sortByType = actionGroup.addAction("Sort by type"); sortByType->setCheckable(true); AZStd::intrusive_ptr settings = AZ::UserSettings::CreateFind(AZ_CRC("ScriptCanvasPreviewSettings", 0x1c5a2965), AZ::UserSettings::CT_LOCAL); if (settings->m_variablePanelSorting == GraphVariablesModel::ColumnIndex::Name) { sortByName->setChecked(true); } else { sortByType->setChecked(true); } QAction* cleanupAction = new QAction(QObject::tr("Remove unused variables"), this); QAction* actionResult = nullptr; // Bring up the context menu if the item is valid if (index.row() > -1) { ScriptCanvas::VariableId varId = index.data(GraphVariablesModel::VarIdRole).value(); VariablePanelContextMenu menu(this, m_scriptCanvasId, varId); menu.addSeparator(); menu.addAction(cleanupAction); menu.addSeparator(); menu.addAction(sortByName); menu.addAction(sortByType); actionResult = menu.exec(ui->graphVariables->mapToGlobal(pos)); } else { QMenu menu; menu.addAction(cleanupAction); menu.addSeparator(); menu.addAction(sortByName); menu.addAction(sortByType); actionResult = menu.exec(ui->graphVariables->mapToGlobal(pos)); } // Very likely the actions are dangling pointers here. Do not dereference them. if (actionResult == sortByName) { settings->m_variablePanelSorting = GraphVariablesModel::ColumnIndex::Name; ui->graphVariables->ApplyPreferenceSort(); } else if (actionResult == sortByType) { settings->m_variablePanelSorting = GraphVariablesModel::ColumnIndex::Type; ui->graphVariables->ApplyPreferenceSort(); } else if (actionResult == cleanupAction) { OnRemoveUnusedVariables(); } } void VariableDockWidget::OnSelectionChanged(const AZStd::unordered_set< ScriptCanvas::VariableId>& variableIds) { if (!variableIds.empty()) { GraphCanvas::SceneRequestBus::Event(m_graphCanvasGraphId, &GraphCanvas::SceneRequests::ClearSelection); AZStd::vector deselectedVariableIds; for (auto pair : m_usedElements) { if (variableIds.count(pair.first) == 0) { deselectedVariableIds.emplace_back(pair.first); m_unusedPool.emplace_back(pair.second); } } for (auto variableId : deselectedVariableIds) { ReleaseComponent(variableId); } } else { ResetPool(); } AZStd::vector selection; ScriptCanvas::RuntimeRequests* runtimeRequests = ScriptCanvas::RuntimeRequestBus::FindFirstHandler(m_scriptCanvasId); if (runtimeRequests == nullptr) { return; } for (const ScriptCanvas::VariableId& varId : variableIds) { VariablePropertiesComponent* propertiesComponent = AllocateComponent(varId); if (propertiesComponent) { ScriptCanvas::GraphVariable* graphVariable = runtimeRequests->FindVariableById(varId);; propertiesComponent->SetVariable(graphVariable); selection.push_back(propertiesComponent->GetEntityId()); } } OnHighlightVariables(variableIds); Q_EMIT OnVariableSelectionChanged(selection); } void VariableDockWidget::OnDuplicateVariable(const ScriptCanvas::VariableId& variableId) { ScriptCanvas::GraphVariable* graphVariable = nullptr; ScriptCanvas::GraphVariableManagerRequestBus::EventResult(graphVariable, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::FindVariableById, variableId); if (graphVariable == nullptr) { return; } ScriptCanvas::GraphVariableManagerRequestBus::Event(m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::CloneVariable, (*graphVariable)); } void VariableDockWidget::OnCreateVariable(ScriptCanvas::Data::Type varType) { AZStd::string varName = FindDefaultVariableName(m_scriptCanvasId); OnCreateNamedVariable(varName, varType); } void VariableDockWidget::OnCreateNamedVariable(const AZStd::string& variableName, ScriptCanvas::Data::Type varType) { ShowGraphVariables(); ScriptCanvas::Datum datum(varType, ScriptCanvas::Datum::eOriginality::Original); AZ::Outcome outcome = AZ::Failure(AZStd::string()); ScriptCanvas::GraphVariableManagerRequestBus::EventResult(outcome, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::AddVariable, variableName, datum); AZ_Warning("VariablePanel", outcome.IsSuccess(), "Could not create new variable: %s", outcome.GetError().c_str()); GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_scriptCanvasId); // We already provide a naming hook for container types so we don't need to do re-force them into it. if (outcome.IsSuccess() && !ScriptCanvas::Data::IsContainerType(varType)) { ui->graphVariables->EditVariableName(outcome.GetValue()); } } void VariableDockWidget::OnDeleteVariables(const AZStd::unordered_set< ScriptCanvas::VariableId>& variableIds) { PropertyGridRequestBus::Broadcast(&PropertyGridRequests::ClearSelection); GeneralRequestBus::Broadcast(&GeneralRequests::PushPreventUndoStateUpdate); bool result = false; for (const ScriptCanvas::VariableId& variableId : variableIds) { if (CanDeleteVariable(variableId)) { ScriptCanvas::GraphVariableManagerRequestBus::EventResult(result, m_scriptCanvasId, &ScriptCanvas::GraphVariableManagerRequests::RemoveVariable, variableId); AZ_Warning("VariablePanel", result, "Could not delete Variable Id (%s).", variableId.ToString().data()); if (result) { ReleaseComponent(variableId); } } } ui->graphVariables->ResizeColumns(); GeneralRequestBus::Broadcast(&GeneralRequests::PopPreventUndoStateUpdate); if (result) { GeneralRequestBus::Broadcast(&GeneralRequests::PostUndoPoint, m_scriptCanvasId); } } void VariableDockWidget::OnHighlightVariables(const AZStd::unordered_set< ScriptCanvas::VariableId>& variableIds) { EditorGraphRequestBus::Event(m_scriptCanvasId, &EditorGraphRequests::HighlightVariables, variableIds); } void VariableDockWidget::OnRemoveUnusedVariables() { EditorGraphRequestBus::Event(m_scriptCanvasId, &EditorGraphRequests::RemoveUnusedVariables); } bool VariableDockWidget::CanDeleteVariable(const ScriptCanvas::VariableId& variableId) { bool canDeleteVariable = false; AZStd::vector< NodeIdPair > nodeIds; EditorGraphRequestBus::EventResult(nodeIds, m_scriptCanvasId, &EditorGraphRequests::GetVariableNodes, variableId); if (!nodeIds.empty()) { AZStd::string variableName; ScriptCanvas::VariableRequestBus::EventResult(variableName, ScriptCanvas::GraphScopedVariableId(m_scriptCanvasId, variableId), &ScriptCanvas::VariableRequests::GetName); int result = QMessageBox::warning(this, QString("Delete %1 and References").arg(variableName.c_str()), QString("The variable \"%1\" has %2 active references.\nAre you sure you want to delete the variable and its references from the graph?").arg(variableName.c_str()).arg(nodeIds.size()), QMessageBox::StandardButton::Yes, QMessageBox::StandardButton::Cancel); if (result == QMessageBox::StandardButton::Yes) { canDeleteVariable = true; AZStd::unordered_set< AZ::EntityId > memberIds; memberIds.reserve(nodeIds.size()); AZStd::unordered_set< ScriptCanvas::VariableId > variableIds = { variableId }; for (auto memberPair : nodeIds) { bool removedReferences = false; ScriptCanvas::NodeRequestBus::EventResult(removedReferences, memberPair.m_scriptCanvasId, &ScriptCanvas::NodeRequests::RemoveVariableReferences, variableIds); // If we didn't remove the references. Just delete the node. if (!removedReferences) { memberIds.insert(memberPair.m_graphCanvasId); } } GraphCanvas::SceneRequestBus::Event(m_graphCanvasGraphId, &GraphCanvas::SceneRequests::Delete, memberIds); } } else { canDeleteVariable = true; } return canDeleteVariable; } VariablePropertiesComponent* VariableDockWidget::AllocateComponent(const ScriptCanvas::VariableId& variableId) { auto elementIter = m_usedElements.find(variableId); if (elementIter != m_usedElements.end()) { return elementIter->second; } if (!m_unusedPool.empty()) { VariablePropertiesComponent* component = m_unusedPool.back(); m_unusedPool.pop_back(); m_usedElements[variableId] = component; return component; } else { m_propertyHelpers.emplace_back(AZStd::unique_ptr(VariablePropertiesComponent::CreateVariablePropertiesEntity())); AZ::Entity* entity = m_propertyHelpers.back().get(); entity->Init(); entity->Activate(); VariablePropertiesComponent* component = AZ::EntityUtils::FindFirstDerivedComponent(entity); if (component) { m_usedElements[variableId] = component; return component; } } return nullptr; } void VariableDockWidget::ReleaseComponent(const ScriptCanvas::VariableId& variableId) { auto mapIter = m_usedElements.find(variableId); if (mapIter != m_usedElements.end()) { m_unusedPool.emplace_back(mapIter->second); m_usedElements.erase(mapIter); } } void VariableDockWidget::ResetPool() { for (auto pair : m_usedElements) { m_unusedPool.emplace_back(pair.second); } m_usedElements.clear(); } #include }