Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,80 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetDataAggregator.h>
namespace ScriptCanvasEditor
{
///////////////////////////////
// LoggingAssetDataAggregator
///////////////////////////////
LoggingAssetDataAggregator::LoggingAssetDataAggregator(const AZ::Data::AssetId& assetId)
: m_assetId(assetId)
{
}
LoggingAssetDataAggregator::~LoggingAssetDataAggregator()
{
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::AnnotateNodeSignal& /**/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::ExecutionThreadEnd& /*loggableEvent*/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::ExecutionThreadBeginning& /*loggableEvent*/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::GraphActivation& /*loggableEvent*/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::GraphDeactivation& /*loggableEvent*/)
{
// call parent process function in the aggregator class
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::NodeStateChange& loggableEvent)
{
ProcessNodeStateChanged(loggableEvent);
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::InputSignal& loggableEvent)
{
ProcessInputSignal(loggableEvent);
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::OutputDataSignal& loggableEvent)
{
ProcessOutputDataSignal(loggableEvent);
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::OutputSignal& loggableEvent)
{
ProcessOutputSignal(loggableEvent);
}
void LoggingAssetDataAggregator::Visit(ScriptCanvas::VariableChange& loggableEvent)
{
ProcessVariableChangedSignal(loggableEvent);
}
}
@@ -0,0 +1,49 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#include <ScriptCanvas/Debugger/Logger.h>
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
namespace ScriptCanvasEditor
{
class LoggingAssetDataAggregator
: public LoggingDataAggregator
, public ScriptCanvas::LoggableEventVisitor
{
public:
AZ_CLASS_ALLOCATOR(LoggingAssetDataAggregator, AZ::SystemAllocator, 0);
LoggingAssetDataAggregator(const AZ::Data::AssetId& assetId);
~LoggingAssetDataAggregator() override;
bool CanCaptureData() const override { return false; }
bool IsCapturingData() const override { return false; }
protected:
void Visit(ScriptCanvas::AnnotateNodeSignal&);
void Visit(ScriptCanvas::ExecutionThreadEnd&);
void Visit(ScriptCanvas::ExecutionThreadBeginning&);
void Visit(ScriptCanvas::GraphActivation&);
void Visit(ScriptCanvas::GraphDeactivation&);
void Visit(ScriptCanvas::NodeStateChange&);
void Visit(ScriptCanvas::InputSignal&);
void Visit(ScriptCanvas::OutputDataSignal&);
void Visit(ScriptCanvas::OutputSignal&);
void Visit(ScriptCanvas::VariableChange&);
private:
AZ::Data::AssetId m_assetId;
};
}
@@ -0,0 +1,53 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetWindowSession.h>
namespace ScriptCanvasEditor
{
//////////////////////////////
// LoggingAssetWindowSession
//////////////////////////////
LoggingAssetWindowSession::LoggingAssetWindowSession(const AZ::Data::AssetId& assetId, QWidget* parent)
: LoggingWindowSession(parent)
, m_dataAggregator(assetId)
, m_assetId(assetId)
{
SetDataId(m_dataAggregator.GetDataId());
m_ui->captureButton->setEnabled(false);
RegisterTreeRoot(m_dataAggregator.GetTreeRoot());
}
LoggingAssetWindowSession::~LoggingAssetWindowSession()
{
}
void LoggingAssetWindowSession::OnCaptureButtonPressed()
{
}
void LoggingAssetWindowSession::OnPlaybackButtonPressed()
{
// TODO
}
void LoggingAssetWindowSession::OnOptionsButtonPressed()
{
// TODO
}
#include <Editor/View/Widgets/LoggingPanel/AssetWindowSession/moc_LoggingAssetWindowSession.cpp>
}
@@ -0,0 +1,44 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowSession.h>
#include <Editor/View/Widgets/LoggingPanel/AssetWindowSession/LoggingAssetDataAggregator.h>
#endif
namespace ScriptCanvasEditor
{
class LoggingAssetWindowSession
: public LoggingWindowSession
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(LoggingAssetWindowSession, AZ::SystemAllocator, 0);
LoggingAssetWindowSession(const AZ::Data::AssetId& assetId, QWidget* parent = nullptr);
~LoggingAssetWindowSession() override;
protected:
void OnCaptureButtonPressed() override;
void OnPlaybackButtonPressed() override;
void OnOptionsButtonPressed() override;
private:
AZ::Data::AssetId m_assetId;
LoggingAssetDataAggregator m_dataAggregator;
};
}
@@ -0,0 +1,396 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h>
#include <ScriptCanvas/Debugger/API.h>
#include <ScriptCanvas/Asset/ExecutionLogAssetBus.h>
namespace ScriptCanvasEditor
{
//////////////////////////////
// LiveLoggingDataAggregator
//////////////////////////////
LiveLoggingDataAggregator::LiveLoggingDataAggregator()
: m_captureType(CaptureType::Editor)
, m_isCapturingData(false)
, m_ignoreRegistrations(false)
{
ScriptCanvas::Debugger::ClientUINotificationBus::Handler::BusConnect();
OnCurrentTargetChanged();
}
LiveLoggingDataAggregator::~LiveLoggingDataAggregator()
{
}
void LiveLoggingDataAggregator::OnCurrentTargetChanged()
{
ResetData();
bool isConnected = false;
ScriptCanvas::Debugger::ClientRequestsBus::BroadcastResult(isConnected, &ScriptCanvas::Debugger::ClientRequests::HasValidConnection);
if (isConnected)
{
EditorLoggingComponentNotificationBus::Handler::BusDisconnect();
if (!ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusIsConnected())
{
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusConnect();
}
bool isSelf = false;
ScriptCanvas::Debugger::ClientRequestsBus::BroadcastResult(isSelf, &ScriptCanvas::Debugger::ClientRequests::IsConnectedToSelf);
if (!isSelf)
{
m_captureType = CaptureType::External;
m_staticRegistrations.clear();
}
}
else
{
if (!EditorLoggingComponentNotificationBus::Handler::BusIsConnected())
{
EditorLoggingComponentNotificationBus::Handler::BusConnect();
}
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusDisconnect();
m_captureType = CaptureType::Editor;
SetupEditorEntities();
}
}
bool LiveLoggingDataAggregator::CanCaptureData() const
{
return true;
}
bool LiveLoggingDataAggregator::IsCapturingData() const
{
return m_isCapturingData;
}
void LiveLoggingDataAggregator::OnEditorScriptCanvasComponentActivated(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_assetId.IsValid())
{
RegisterScriptCanvas(namedEntityId, graphIdentifier);
}
}
void LiveLoggingDataAggregator::OnEditorScriptCanvasComponentDeactivated(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
UnregisterScriptCanvas(namedEntityId, graphIdentifier);
}
void LiveLoggingDataAggregator::OnAssetSwitched(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& newGraphIdentifier, const ScriptCanvas::GraphIdentifier& oldGraphIdentifier)
{
if (newGraphIdentifier == oldGraphIdentifier)
{
return;
}
if (newGraphIdentifier.m_assetId.IsValid())
{
RegisterScriptCanvas(namedEntityId, newGraphIdentifier);
}
UnregisterScriptCanvas(namedEntityId, oldGraphIdentifier);
RemoveStaticRegistration(namedEntityId, oldGraphIdentifier);
}
void LiveLoggingDataAggregator::Connected([[maybe_unused]] const ScriptCanvas::Debugger::Target& target)
{
AZStd::lock(m_notificationMutex);
SetupExternalEntities();
}
void LiveLoggingDataAggregator::GraphActivated(const ScriptCanvas::GraphActivation& activationSignal)
{
AZStd::lock(m_notificationMutex);
RegisterScriptCanvas(activationSignal.m_runtimeEntity, activationSignal.m_graphIdentifier);
RegisterEntityName(activationSignal.m_runtimeEntity, activationSignal.m_runtimeEntity.GetName());
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, activationSignal.m_entityIsObserved, activationSignal.m_runtimeEntity, activationSignal.m_graphIdentifier);
}
void LiveLoggingDataAggregator::GraphDeactivated(const ScriptCanvas::GraphDeactivation& deactivationSignal)
{
AZStd::lock(m_notificationMutex);
UnregisterScriptCanvas(deactivationSignal.m_runtimeEntity, deactivationSignal.m_graphIdentifier);
}
void LiveLoggingDataAggregator::NodeStateChanged(const ScriptCanvas::NodeStateChange& nodeStateChangeSignal)
{
AZStd::lock(m_notificationMutex);
ProcessNodeStateChanged(nodeStateChangeSignal);
}
void LiveLoggingDataAggregator::SignaledInput(const ScriptCanvas::InputSignal& inputSignal)
{
AZStd::lock(m_notificationMutex);
ProcessInputSignal(inputSignal);
}
void LiveLoggingDataAggregator::SignaledOutput(const ScriptCanvas::OutputSignal& outputSignal)
{
AZStd::lock(m_notificationMutex);
ProcessOutputSignal(outputSignal);
}
void LiveLoggingDataAggregator::SignaledDataOutput(const ScriptCanvas::OutputDataSignal& outputDataSignal)
{
AZStd::lock(m_notificationMutex);
ProcessOutputDataSignal(outputDataSignal);
}
void LiveLoggingDataAggregator::AnnotateNode(const ScriptCanvas::AnnotateNodeSignal& annotateNode)
{
AZStd::lock(m_notificationMutex);
ProcessAnnotateNode(annotateNode);
}
void LiveLoggingDataAggregator::VariableChanged(const ScriptCanvas::VariableChange& variableChangeSignal)
{
AZStd::lock(m_notificationMutex);
ProcessVariableChangedSignal(variableChangeSignal);
}
void LiveLoggingDataAggregator::GetActiveEntitiesResult(const ScriptCanvas::ActiveEntityStatusMap& activeEntities)
{
AZStd::lock(m_notificationMutex);
m_ignoreRegistrations = true;
for (const auto& activeEntityPair : activeEntities)
{
const AZ::NamedEntityId& namedEntityId = activeEntityPair.first;
RegisterEntityName(namedEntityId, namedEntityId.GetName());
const auto& activeEntityStatus = activeEntityPair.second;
for (const auto& activeGraphStatus : activeEntityStatus.m_activeGraphs)
{
RegisterScriptCanvas(namedEntityId, activeGraphStatus.first);
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, activeGraphStatus.second.m_isObserved, namedEntityId, activeGraphStatus.first);
}
}
m_ignoreRegistrations = false;
}
const AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier>& LiveLoggingDataAggregator::GetStaticRegistrations() const
{
return m_staticRegistrations;
}
void LiveLoggingDataAggregator::OnRegistrationEnabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (m_ignoreRegistrations)
{
return;
}
if (IsCapturingData() || m_captureType == External)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::AddGraphLoggingTarget, graphIdentifier.m_assetId);
}
else
{
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::AddEntityLoggingTarget, namedEntityId, graphIdentifier);
bool gotResult = false;
AZ::EntityId editorId;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(gotResult, &AzToolsFramework::EditorEntityContextRequests::MapRuntimeIdToEditorId, namedEntityId, editorId);
if (gotResult)
{
AZ::NamedEntityId namedEditorId(editorId, namedEntityId.GetName());
AddStaticRegistration(namedEditorId, graphIdentifier);
}
}
return;
}
AddStaticRegistration(namedEntityId, graphIdentifier);
}
void LiveLoggingDataAggregator::OnRegistrationDisabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (m_ignoreRegistrations)
{
return;
}
if (IsCapturingData() || m_captureType == External)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::RemoveGraphLoggingTarget, graphIdentifier.m_assetId);
}
else
{
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::RemoveEntityLoggingTarget, namedEntityId, graphIdentifier);
if (m_captureType == Editor)
{
bool gotResult = false;
AZ::EntityId editorId;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(gotResult, &AzToolsFramework::EditorEntityContextRequests::MapRuntimeIdToEditorId, namedEntityId, editorId);
if (gotResult)
{
AZ::NamedEntityId namedEditorId(editorId, namedEntityId.GetName());
RemoveStaticRegistration(namedEditorId, graphIdentifier);
}
}
}
return;
}
RemoveStaticRegistration(namedEntityId, graphIdentifier);
}
void LiveLoggingDataAggregator::AddStaticRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId
|| m_captureType != Editor)
{
return;
}
bool registerEvent = true;
auto mapRange = m_staticRegistrations.equal_range(namedEntityId);
for (auto mapIter = mapRange.first; mapIter != mapRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
registerEvent = false;
break;
}
}
if (registerEvent)
{
m_staticRegistrations.insert(AZStd::make_pair(namedEntityId, graphIdentifier));
}
}
void LiveLoggingDataAggregator::RemoveStaticRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId
|| m_captureType != Editor)
{
return;
}
auto mapRange = m_staticRegistrations.equal_range(namedEntityId);
for (auto mapIter = mapRange.first; mapIter != mapRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
m_staticRegistrations.erase(mapIter);
break;
}
}
}
void LiveLoggingDataAggregator::SetupEditorEntities()
{
m_ignoreRegistrations = true;
EditorScriptCanvasComponentLoggingBus::EnumerateHandlers([this](EditorScriptCanvasComponentLogging* loggingComponent)
{
ScriptCanvas::GraphIdentifier graphIdentifier = loggingComponent->GetGraphIdentifier();
if (graphIdentifier.m_assetId.IsValid())
{
RegisterScriptCanvas(loggingComponent->FindNamedEntityId(), graphIdentifier);
}
return true;
});
for (const auto& mapPair : m_staticRegistrations)
{
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, true, mapPair.first, mapPair.second);
}
m_ignoreRegistrations = false;
}
void LiveLoggingDataAggregator::SetupExternalEntities()
{
ScriptCanvas::Debugger::ClientRequestsBus::Broadcast(&ScriptCanvas::Debugger::ClientRequests::GetActiveEntities);
}
void LiveLoggingDataAggregator::StartCaptureData()
{
AZStd::lock(m_notificationMutex);
m_isCapturingData = true;
ResetLog();
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusConnect();
}
void LiveLoggingDataAggregator::StopCaptureData()
{
AZStd::lock(m_notificationMutex);
m_isCapturingData = false;
ResetData();
const AZStd::string name = AZStd::string::format("ScriptCanvasLog_%s", AZStd::to_string(AZStd::GetTimeUTCMilliSecond()).data());
ScriptCanvas::ExecutionLogAssetEBus::Broadcast(&ScriptCanvas::ExecutionLogAssetBus::SaveToRelativePath, name);
if (m_captureType == CaptureType::Editor)
{
bool isDesiredTargetConnected = false;
AzFramework::TargetManager::Bus::BroadcastResult(isDesiredTargetConnected, &AzFramework::TargetManager::IsDesiredTargetOnline);
if (isDesiredTargetConnected)
{
SetupExternalEntities();
}
else
{
SetupEditorEntities();
}
}
else
{
SetupExternalEntities();
}
ScriptCanvas::ExecutionLogAssetEBus::Broadcast(&ScriptCanvas::ExecutionLogAssetBus::ClearLog);
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusDisconnect();
}
}
@@ -0,0 +1,99 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/TargetManagement/TargetManagementAPI.h>
#include <Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#include <ScriptCanvas/Debugger/Bus.h>
#include <ScriptCanvas/Debugger/Logger.h>
namespace ScriptCanvasEditor
{
class LiveLoggingDataAggregator
: public LoggingDataAggregator
, public EditorLoggingComponentNotificationBus::Handler
, public ScriptCanvas::Debugger::ServiceNotificationsBus::Handler
, public ScriptCanvas::Debugger::ClientUINotificationBus::Handler
{
enum CaptureType
{
Editor,
External
};
public:
AZ_CLASS_ALLOCATOR(LiveLoggingDataAggregator, AZ::SystemAllocator, 0);
LiveLoggingDataAggregator();
~LiveLoggingDataAggregator();
// ClientUINotificationBus
void OnCurrentTargetChanged() override;
////
bool CanCaptureData() const;
bool IsCapturingData() const;
void StartCaptureData();
void StopCaptureData();
// EditorLoggingComponentNotifications
void OnEditorScriptCanvasComponentActivated(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void OnEditorScriptCanvasComponentDeactivated(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void OnAssetSwitched(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& newAssetId, const ScriptCanvas::GraphIdentifier& oldAssetId) override;
////
// ServiceNotifications
//// Logging Notifications
void Connected(const ScriptCanvas::Debugger::Target& target) override;
void GraphActivated(const ScriptCanvas::GraphActivation& activatedSignal) override;
void GraphDeactivated(const ScriptCanvas::GraphDeactivation& deactivatedSignal) override;
void NodeStateChanged(const ScriptCanvas::NodeStateChange& stateChange) override;
void SignaledInput(const ScriptCanvas::InputSignal& inputSignal) override;
void SignaledOutput(const ScriptCanvas::OutputSignal& outputSignal) override;
void SignaledDataOutput(const ScriptCanvas::OutputDataSignal&) override;
void AnnotateNode(const ScriptCanvas::AnnotateNodeSignal& annotateNode) override;
void VariableChanged(const ScriptCanvas::VariableChange& variableChanged) override;
//// Result Methods
void GetActiveEntitiesResult(const ScriptCanvas::ActiveEntityStatusMap& activeEntityMap) override;
////
const AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier>& GetStaticRegistrations() const;
protected:
void OnRegistrationEnabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void OnRegistrationDisabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
private:
void AddStaticRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void RemoveStaticRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void SetupEditorEntities();
void SetupExternalEntities();
CaptureType m_captureType;
bool m_isCapturingData;
bool m_ignoreRegistrations;
AZStd::recursive_mutex m_notificationMutex;
ScriptCanvas::Debugger::Logger m_logger;
AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier> m_staticRegistrations;
};
}
@@ -0,0 +1,549 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <EditorCoreAPI.h>
#include <IEditor.h>
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingWindowSession.h>
namespace ScriptCanvasEditor
{
///////////////////////
// TargetManagerModel
///////////////////////
TargetManagerModel::TargetManagerModel()
{
AzFramework::TargetInfo editorTargetInfo(0, "Editor");
m_targetInfo.push_back(editorTargetInfo);
AzFramework::TargetManager::Bus::BroadcastResult(m_selfInfo, &AzFramework::TargetManager::GetMyTargetInfo);
ScrapeTargetInfo();
}
int TargetManagerModel::rowCount([[maybe_unused]] const QModelIndex& parent) const
{
return static_cast<int>(m_targetInfo.size());
}
QVariant TargetManagerModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
switch (role)
{
case Qt::DisplayRole:
{
const AzFramework::TargetInfo& targetInfo = m_targetInfo[index.row()];
if (index.row() > 0)
{
return QString("%1 (%2)").arg(targetInfo.GetDisplayName(), QString::number(targetInfo.GetPersistentId(), 16));
}
else
{
return QString(targetInfo.GetDisplayName());
}
}
break;
default:
break;
}
return QVariant();
}
void TargetManagerModel::TargetJoinedNetwork(AzFramework::TargetInfo info)
{
if (!info.IsIdentityEqualTo(m_selfInfo))
{
int element = GetRowForTarget(info.GetPersistentId());
if (element < 0)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_targetInfo.push_back(info);
endInsertRows();
}
}
else
{
ScrapeTargetInfo();
}
}
void TargetManagerModel::TargetLeftNetwork(AzFramework::TargetInfo info)
{
int element = GetRowForTarget(info.GetPersistentId());
// 0 is reserved for our fake Editor one.
// And we don't want to remove it.
if (element > 0)
{
beginRemoveRows(QModelIndex(), element, element);
m_targetInfo.erase(m_targetInfo.begin() + element);
endRemoveRows();
}
}
AzFramework::TargetInfo TargetManagerModel::FindTargetInfoForRow(int row)
{
if (row < 0 && row >= m_targetInfo.size())
{
return AzFramework::TargetInfo();
}
return m_targetInfo[row];
}
int TargetManagerModel::GetRowForTarget(AZ::u32 targetId)
{
for (size_t i = 0; i < m_targetInfo.size(); ++i)
{
if (m_targetInfo[i].GetPersistentId() == targetId)
{
return static_cast<int>(i);
}
}
return -1;
}
void TargetManagerModel::ScrapeTargetInfo()
{
AzFramework::TargetContainer targets;
AzFramework::TargetManager::Bus::Broadcast(&AzFramework::TargetManager::EnumTargetInfos, targets);
for (const auto& targetPair : targets)
{
if (!targetPair.second.IsIdentityEqualTo(m_selfInfo))
{
m_targetInfo.push_back(targetPair.second);
}
}
}
////////////////////////////
// LiveLoggingUserSettings
////////////////////////////
AZStd::intrusive_ptr<LiveLoggingUserSettings> LiveLoggingUserSettings::FindSettingsInstance()
{
return AZ::UserSettings::CreateFind<LiveLoggingUserSettings>(AZ_CRC("ScriptCanvas::LiveLoggingUserSettings", 0xc79efe7b), AZ::UserSettings::CT_LOCAL);
}
void LiveLoggingUserSettings::Reflect(AZ::ReflectContext* reflectContext)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflectContext);
if (serializeContext)
{
serializeContext->Class<LiveLoggingUserSettings>()
->Version(1)
->Field("AutoCapturing", &LiveLoggingUserSettings::m_isAutoCaptureEnabled)
->Field("LiveUpdating", &LiveLoggingUserSettings::m_enableLiveUpdates)
;
}
}
void LiveLoggingUserSettings::SetAutoCaptureEnabled(bool enabled)
{
m_isAutoCaptureEnabled = enabled;
}
bool LiveLoggingUserSettings::IsAutoCaptureEnabled() const
{
return m_isAutoCaptureEnabled;
}
void LiveLoggingUserSettings::SetLiveUpdates(bool enabled)
{
m_enableLiveUpdates = enabled;
}
bool LiveLoggingUserSettings::IsLiveUpdating() const
{
return m_enableLiveUpdates;
}
/////////////////////////////
// LiveLoggingWindowSession
/////////////////////////////
LiveLoggingWindowSession::LiveLoggingWindowSession(QWidget* parent)
: LoggingWindowSession(parent)
, m_startedSession(false)
, m_encodeStaticEntities(false)
, m_isCapturing(false)
{
AzFramework::TargetManagerClient::Bus::Handler::BusConnect();
m_targetManagerModel = aznew TargetManagerModel();
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
m_ui->targetSelector->setModel(m_targetManagerModel);
}
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusConnect();
SetDataId(m_liveDataAggregator.GetDataId());
RegisterTreeRoot(m_liveDataAggregator.GetTreeRoot());
m_userSettings = LiveLoggingUserSettings::FindSettingsInstance();
if (!m_userSettings->IsLiveUpdating())
{
m_liveDataAggregator.GetTreeRoot()->SetUpdatePolicy(DebugLogRootItem::UpdatePolicy::SingleTime);
}
else
{
m_liveDataAggregator.GetTreeRoot()->SetUpdatePolicy(DebugLogRootItem::UpdatePolicy::RealTime);
}
// Despite being apart of the base menu for now, the LiveLoggingWindow is the only one that needs to utilize these buttons.
// Going to control them from here.
m_ui->liveUpdatesToggle->setChecked(m_userSettings->IsLiveUpdating());
QObject::connect(m_ui->liveUpdatesToggle, &QToolButton::toggled, this, &LiveLoggingWindowSession::OnLiveUpdateToggled);
m_ui->autoCaptureToggle->setChecked(m_userSettings->IsAutoCaptureEnabled());
QObject::connect(m_ui->autoCaptureToggle, &QToolButton::toggled, this, &LiveLoggingWindowSession::OnAutoCaptureToggled);
}
LiveLoggingWindowSession::~LiveLoggingWindowSession()
{
AzFramework::TargetManagerClient::Bus::Handler::BusDisconnect();
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
ScriptCanvas::Debugger::ServiceNotificationsBus::Handler::BusDisconnect();
}
void LiveLoggingWindowSession::DesiredTargetChanged(AZ::u32 newId, [[maybe_unused]] AZ::u32 oldId)
{
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
int row = m_targetManagerModel->GetRowForTarget(newId);
if (row < 0)
{
m_ui->targetSelector->setCurrentIndex(0);
}
else
{
m_ui->targetSelector->setCurrentIndex(row);
}
}
}
void LiveLoggingWindowSession::DesiredTargetConnected(bool connected)
{
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
bool useFallback = !connected;
if (connected)
{
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
AzFramework::TargetInfo desiredInfo;
AzFramework::TargetManager::Bus::BroadcastResult(desiredInfo, &AzFramework::TargetManager::GetDesiredTarget);
if (desiredInfo.IsValid() && !desiredInfo.IsSelf())
{
int index = m_targetManagerModel->GetRowForTarget(desiredInfo.GetPersistentId());
if (index > 0)
{
m_ui->targetSelector->setCurrentIndex(index);
}
}
else
{
useFallback = true;
}
}
else if (m_isCapturing)
{
SetIsCapturing(false);
}
if (useFallback)
{
if (!AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusIsConnected())
{
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
}
m_ui->targetSelector->setCurrentIndex(0);
}
}
}
void LiveLoggingWindowSession::TargetJoinedNetwork(AzFramework::TargetInfo info)
{
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
m_targetManagerModel->TargetJoinedNetwork(info);
}
}
void LiveLoggingWindowSession::TargetLeftNetwork(AzFramework::TargetInfo info)
{
{
QSignalBlocker signalBlocker(m_ui->targetSelector);
m_targetManagerModel->TargetLeftNetwork(info);
}
}
void LiveLoggingWindowSession::OnStartPlayInEditorBegin()
{
if (isVisible())
{
m_encodeStaticEntities = true;
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::StartEditorSession);
if ((m_userSettings->IsAutoCaptureEnabled()) || m_startedSession)
{
SetIsCapturing(true);
}
}
}
void LiveLoggingWindowSession::OnStopPlayInEditor()
{
if (isVisible())
{
SetIsCapturing(false);
m_startedSession = false;
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::StopEditorSession);
m_encodeStaticEntities = false;
}
}
void LiveLoggingWindowSession::Connected([[maybe_unused]] const ScriptCanvas::Debugger::Target& target)
{
if (m_userSettings->IsAutoCaptureEnabled() && isVisible())
{
SetIsCapturing(true);
}
}
void LiveLoggingWindowSession::OnCaptureButtonPressed()
{
bool isSelfTarget = false;
ScriptCanvas::Debugger::ClientRequestsBus::BroadcastResult(isSelfTarget, &ScriptCanvas::Debugger::ClientRequests::IsConnectedToSelf);
if (isSelfTarget)
{
if (!m_startedSession)
{
bool isRunningGame = false;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(isRunningGame, &AzToolsFramework::EditorEntityContextRequests::IsEditorRunningGame);
if (!isRunningGame)
{
m_startedSession = true;
GetIEditor()->SetInGameMode(true);
return;
}
}
else
{
GetIEditor()->SetInGameMode(false);
return;
}
}
SetIsCapturing(!m_isCapturing);
}
void LiveLoggingWindowSession::OnPlaybackButtonPressed()
{
// Nothing to do in the LiveLoggingWindowSession
}
void LiveLoggingWindowSession::OnOptionsButtonPressed()
{
QPoint point = QCursor::pos();
QMenu optionsMenu;
QAction* autoCaptureAction = optionsMenu.addAction("Auto Capture");
autoCaptureAction->setCheckable(true);
autoCaptureAction->setChecked(m_userSettings->IsAutoCaptureEnabled());
QObject::connect(autoCaptureAction, &QAction::toggled, this, &LiveLoggingWindowSession::OnAutoCaptureToggled);
QAction* liveUpdateAction = optionsMenu.addAction("Live Updates");
liveUpdateAction->setCheckable(true);
liveUpdateAction->setChecked(m_userSettings->IsLiveUpdating());
QObject::connect(liveUpdateAction, &QAction::toggled, this, &LiveLoggingWindowSession::OnLiveUpdateToggled);
optionsMenu.exec(point);
}
void LiveLoggingWindowSession::OnTargetChanged(int index)
{
// Special case out the editor
if (index == 0)
{
AzFramework::TargetManager::Bus::Broadcast(&AzFramework::TargetManager::SetDesiredTarget, 0);
}
else
{
AzFramework::TargetInfo info = m_targetManagerModel->FindTargetInfoForRow(index);
if (info.IsValid())
{
AzFramework::TargetManager::Bus::Broadcast(&AzFramework::TargetManager::SetDesiredTarget, info.GetNetworkId());
}
}
}
void LiveLoggingWindowSession::OnAutoCaptureToggled(bool checked)
{
m_userSettings->SetAutoCaptureEnabled(checked);
}
void LiveLoggingWindowSession::OnLiveUpdateToggled(bool checked)
{
m_userSettings->SetLiveUpdates(checked);
if (!m_userSettings->IsLiveUpdating())
{
m_liveDataAggregator.GetTreeRoot()->SetUpdatePolicy(DebugLogRootItem::UpdatePolicy::SingleTime);
}
else
{
// If we enable this we want to update the current display.
m_liveDataAggregator.GetTreeRoot()->RedoLayout();
m_liveDataAggregator.GetTreeRoot()->SetUpdatePolicy(DebugLogRootItem::UpdatePolicy::RealTime);
}
}
void LiveLoggingWindowSession::StartDataCapture()
{
ScriptCanvas::Debugger::ScriptTarget captureInfo;
ConfigureScriptTarget(captureInfo);
m_liveDataAggregator.StartCaptureData();
m_ui->captureButton->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/capture_live.png"));
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::StartLogging, captureInfo);
}
void LiveLoggingWindowSession::StopDataCapture()
{
m_liveDataAggregator.StopCaptureData();
m_ui->captureButton->setIcon(QIcon(":/ScriptCanvasEditorResources/Resources/capture_offline.png"));
ScriptCanvas::Debugger::ClientUIRequestBus::Broadcast(&ScriptCanvas::Debugger::ClientUIRequests::StopLogging);
if (!m_userSettings->IsLiveUpdating())
{
m_liveDataAggregator.GetTreeRoot()->RedoLayout();
}
}
void LiveLoggingWindowSession::ConfigureScriptTarget(ScriptCanvas::Debugger::ScriptTarget& captureInfo)
{
if (m_encodeStaticEntities)
{
const auto& staticRegistrations = m_liveDataAggregator.GetStaticRegistrations();
for (const auto& registrationPair : staticRegistrations)
{
bool gotResult = false;
AZ::EntityId runtimeId;
AzToolsFramework::EditorEntityContextRequestBus::BroadcastResult(gotResult, &AzToolsFramework::EditorEntityContextRequests::MapEditorIdToRuntimeId, registrationPair.first, runtimeId);
if (runtimeId.IsValid())
{
auto entityIter = captureInfo.m_entities.find(runtimeId);
if (entityIter == captureInfo.m_entities.end())
{
auto insertResult = captureInfo.m_entities.insert(AZStd::make_pair(runtimeId, AZStd::unordered_set< ScriptCanvas::GraphIdentifier >()));
entityIter = insertResult.first;
}
entityIter->second.insert(registrationPair.second);
m_liveDataAggregator.RegisterEntityName(runtimeId, registrationPair.first.GetName());
}
else
{
auto insertResult = captureInfo.m_staticEntities.insert(registrationPair.first);
insertResult.first->second.insert(registrationPair.second);
}
}
}
const LoggingEntityMap& registrationMap = m_liveDataAggregator.GetLoggingEntityMap();
for (const auto& registrationPair : registrationMap)
{
auto entityIter = captureInfo.m_entities.find(registrationPair.first);
if (entityIter == captureInfo.m_entities.end())
{
auto insertResult = captureInfo.m_entities.insert(AZStd::make_pair(registrationPair.first, AZStd::unordered_set< ScriptCanvas::GraphIdentifier >()));
entityIter = insertResult.first;
}
entityIter->second.insert(registrationPair.second);
}
const LoggingAssetSet& registrationSet = m_liveDataAggregator.GetLoggingAssetSet();
for (const auto& graphIdentifier : registrationSet)
{
captureInfo.m_graphs.insert(graphIdentifier.m_assetId);
}
}
void LiveLoggingWindowSession::SetIsCapturing(bool isCapturing)
{
if (isCapturing != m_isCapturing)
{
m_isCapturing = isCapturing;
if (m_isCapturing)
{
StartDataCapture();
}
else
{
StopDataCapture();
}
}
}
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/moc_LiveLoggingWindowSession.cpp>
}
@@ -0,0 +1,143 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QAbstractListModel>
#include <AzCore/UserSettings/UserSettings.h>
#include <AzFramework/TargetManagement/TargetManagementAPI.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowSession.h>
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h>
#endif
namespace ScriptCanvasEditor
{
class TargetManagerModel
: public QAbstractListModel
{
public:
AZ_CLASS_ALLOCATOR(TargetManagerModel, AZ::SystemAllocator, 0);
TargetManagerModel();
// QAbstarctItemModel
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
////
void TargetJoinedNetwork(AzFramework::TargetInfo info);
void TargetLeftNetwork(AzFramework::TargetInfo info);
AzFramework::TargetInfo FindTargetInfoForRow(int row);
int GetRowForTarget(AZ::u32 targetId);
private:
void ScrapeTargetInfo();
AzFramework::TargetInfo m_selfInfo;
AZStd::vector< AzFramework::TargetInfo > m_targetInfo;
};
class LiveLoggingUserSettings
: public AZ::UserSettings
{
public:
static AZStd::intrusive_ptr<LiveLoggingUserSettings> FindSettingsInstance();
AZ_RTTI(LiveLoggingUserSettings, "{2E32C949-5766-480D-B569-781BE9166B2E}", AZ::UserSettings);
AZ_CLASS_ALLOCATOR(LiveLoggingUserSettings, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* reflectContext);
LiveLoggingUserSettings() = default;
void SetAutoCaptureEnabled(bool enabled);
bool IsAutoCaptureEnabled() const;
void SetLiveUpdates(bool enabled);
bool IsLiveUpdating() const;
private:
bool m_isAutoCaptureEnabled = true;
bool m_enableLiveUpdates = true;
};
class LiveLoggingWindowSession
: public LoggingWindowSession
, public AzFramework::TargetManagerClient::Bus::Handler
, public AzToolsFramework::EditorEntityContextNotificationBus::Handler
, public ScriptCanvas::Debugger::ServiceNotificationsBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(LiveLoggingWindowSession, AZ::SystemAllocator, 0);
LiveLoggingWindowSession(QWidget* parent = nullptr);
~LiveLoggingWindowSession() override;
// AzFramework::TargetManagerClient
void DesiredTargetChanged(AZ::u32 newId, AZ::u32 oldId) override;
void DesiredTargetConnected(bool connected) override;
void TargetJoinedNetwork(AzFramework::TargetInfo info) override;
void TargetLeftNetwork(AzFramework::TargetInfo info) override;
////
// AzToolsFramework::EditorEntityContextNotificationBus::Handler
void OnStartPlayInEditorBegin();
void OnStopPlayInEditor();
////
// ScriptCavnas::Debugger::ServiceNotificationsBus
void Connected(const ScriptCanvas::Debugger::Target& target) override;
////
protected:
void OnCaptureButtonPressed() override;
void OnPlaybackButtonPressed() override;
void OnOptionsButtonPressed() override;
void OnTargetChanged(int currentIndex) override;
private:
void OnAutoCaptureToggled(bool checked);
void OnLiveUpdateToggled(bool checked);
void StartDataCapture();
void StopDataCapture();
void ConfigureScriptTarget(ScriptCanvas::Debugger::ScriptTarget& captureInfo);
void SetIsCapturing(bool isCapturing);
TargetManagerModel* m_targetManagerModel;
bool m_startedSession;
bool m_encodeStaticEntities;
bool m_isCapturing;
LiveLoggingDataAggregator m_liveDataAggregator;
ScriptCanvas::Debugger::Target m_targetConfiguration;
AZStd::intrusive_ptr<LiveLoggingUserSettings> m_userSettings;
};
}
@@ -0,0 +1,423 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
namespace ScriptCanvasEditor
{
//////////////////////////
// LoggingDataAggregator
//////////////////////////
LoggingDataAggregator::LoggingDataAggregator()
: m_id(AZ::Entity::MakeId())
, m_ignoreRegistrations(false)
, m_hasAnchor(false)
, m_anchorTimeStamp(0)
{
LoggingDataRequestBus::Handler::BusConnect(m_id);
m_debugLogRoot = aznew DebugLogRootItem();
}
LoggingDataAggregator::~LoggingDataAggregator()
{
}
const LoggingDataId& LoggingDataAggregator::GetDataId() const
{
return m_id;
}
const LoggingDataAggregator* LoggingDataAggregator::FindLoggingData() const
{
return this;
}
void LoggingDataAggregator::EnableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (m_ignoreRegistrations)
{
return;
}
bool signalAddition = false;
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
auto insertResult = m_loggedAssetSet.insert(graphIdentifier);
signalAddition = insertResult.second;
}
else
{
signalAddition = true;
auto equalRange = m_loggingEntityMapping.equal_range(namedEntityId);
for (auto mapIter = equalRange.first; mapIter != equalRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
signalAddition = false;
break;
}
}
if (signalAddition)
{
m_loggingEntityMapping.insert(AZStd::make_pair(namedEntityId, graphIdentifier));
}
}
if (signalAddition)
{
m_ignoreRegistrations = true;
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, true, namedEntityId, graphIdentifier);
m_ignoreRegistrations = false;
OnRegistrationEnabled(namedEntityId, graphIdentifier);
}
}
void LoggingDataAggregator::DisableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (m_ignoreRegistrations)
{
return;
}
bool signalErase = false;
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
size_t eraseCount = m_loggedAssetSet.erase(graphIdentifier);
signalErase = eraseCount > 0;
}
else
{
auto equalRange = m_loggingEntityMapping.equal_range(namedEntityId);
for (auto mapIter = equalRange.first; mapIter != equalRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
signalErase = true;
m_loggingEntityMapping.erase(mapIter);
break;
}
}
}
if (signalErase)
{
m_ignoreRegistrations = true;
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEnabledStateChanged, false, namedEntityId, graphIdentifier);
m_ignoreRegistrations = false;
OnRegistrationDisabled(namedEntityId, graphIdentifier);
}
}
AZ::NamedEntityId LoggingDataAggregator::FindNamedEntityId(const AZ::EntityId& entityId)
{
auto cacheIter = m_entityNameCache.find(entityId);
if (cacheIter != m_entityNameCache.end())
{
return AZ::NamedEntityId(entityId, cacheIter->second);
}
return AZ::NamedEntityId(entityId, "<unknown>");
}
const EntityGraphRegistrationMap& LoggingDataAggregator::GetEntityGraphRegistrationMap() const
{
return m_registrationMap;
}
const LoggingEntityMap& LoggingDataAggregator::GetLoggingEntityMap() const
{
return m_loggingEntityMapping;
}
const LoggingAssetSet& LoggingDataAggregator::GetLoggingAssetSet() const
{
return m_loggedAssetSet;
}
void LoggingDataAggregator::ProcessSignal([[maybe_unused]] const ScriptCanvas::Signal& signal)
{
//GraphIdentifier identifier;
//identifier.m_entityId = signal.m_runtimeEntity;
//identifier.m_assetId = signal.m_graphCount.m_assetId;
//identifier.m_sequenceId = signal.m_graphCount.m_count;
//auto aggregateIter = m_lastAggregateItemMap.find(identifier);
//if (aggregateIter != m_lastAggregateItemMap.end())
//{
// /*
// if (aggregateIter->second->TryProcessSignal(signal))
// {
// return;
// }
// */
// m_lastAggregateItemMap.erase(identifier);
//}
// Signal events are ambiguous on their own. Will need a secondary source of information to be able to disambiguate them.
}
void LoggingDataAggregator::ProcessNodeStateChanged([[maybe_unused]] const ScriptCanvas::NodeStateChange& stateChangeSignal)
{
}
void LoggingDataAggregator::ProcessInputSignal(const ScriptCanvas::InputSignal& inputSignal)
{
if (!m_hasAnchor)
{
m_hasAnchor = true;
m_anchorTimeStamp = inputSignal.GetTimestamp();
}
// For every input we always want to make a new element.
ScriptCanvas::Timestamp relativeTimeStamp = inputSignal.GetTimestamp() - m_anchorTimeStamp;
ExecutionLogTreeItem* treeItem = m_debugLogRoot->CreateExecutionItem(GetDataId(), inputSignal.m_nodeType, inputSignal, inputSignal.m_endpoint.GetNamedNodeId());
m_lastAggregateItemMap[inputSignal] = treeItem;
treeItem->RegisterExecutionInput(ScriptCanvas::Endpoint(), inputSignal.m_endpoint.GetSlotId(), inputSignal.m_endpoint.GetSlotName(), AZStd::chrono::milliseconds(relativeTimeStamp));
for (auto dataMap : inputSignal.m_data)
{
AZStd::string valueString = dataMap.second.m_datum.ToString();
treeItem->RegisterDataInput(ScriptCanvas::Endpoint(), dataMap.first, dataMap.first.m_name, valueString, GetTreeRoot()->GetUpdatePolicy() == DebugLogRootItem::UpdatePolicy::RealTime);
}
}
void LoggingDataAggregator::ProcessOutputSignal(const ScriptCanvas::OutputSignal& outputSignal)
{
if (!m_hasAnchor)
{
m_hasAnchor = true;
m_anchorTimeStamp = outputSignal.GetTimestamp();
}
// For the output we want to correlate it with the appropriate starting node
auto lastAggregateIter = m_lastAggregateItemMap.find(outputSignal);
ExecutionLogTreeItem* treeItem = nullptr;
if (lastAggregateIter != m_lastAggregateItemMap.end())
{
treeItem = lastAggregateIter->second;
if (treeItem->HasExecutionOutput()
|| treeItem->GetNodeId() != outputSignal.m_endpoint.GetNodeId())
{
treeItem = nullptr;
}
}
if (treeItem == nullptr)
{
treeItem = m_debugLogRoot->CreateExecutionItem(GetDataId(), outputSignal.m_nodeType, outputSignal, outputSignal.m_endpoint.GetNamedNodeId());
m_lastAggregateItemMap[outputSignal] = treeItem;
}
ScriptCanvas::Timestamp relativeTimeStamp = outputSignal.GetTimestamp() - m_anchorTimeStamp;
treeItem->RegisterExecutionOutput(outputSignal.m_endpoint.GetSlotId(), outputSignal.m_endpoint.GetSlotName(), AZStd::chrono::milliseconds(relativeTimeStamp));
for (auto dataMap : outputSignal.m_data)
{
AZStd::string valueString = dataMap.second.m_datum.ToString();
treeItem->RegisterDataOutput(dataMap.first, dataMap.first.m_name, valueString, GetTreeRoot()->GetUpdatePolicy() == DebugLogRootItem::UpdatePolicy::RealTime);
}
}
void LoggingDataAggregator::ProcessOutputDataSignal(const ScriptCanvas::OutputDataSignal& outputDataSignal)
{
if (!m_hasAnchor)
{
m_hasAnchor = true;
m_anchorTimeStamp = outputDataSignal.GetTimestamp();
}
// For the output we want to correlate it with the appropriate starting node
auto lastAggregateIter = m_lastAggregateItemMap.find(outputDataSignal);
ExecutionLogTreeItem* treeItem = nullptr;
if (lastAggregateIter != m_lastAggregateItemMap.end())
{
treeItem = lastAggregateIter->second;
if (treeItem->GetNodeId() != outputDataSignal.m_endpoint.GetNodeId())
{
treeItem = nullptr;
}
}
if (treeItem == nullptr)
{
treeItem = m_debugLogRoot->CreateExecutionItem(GetDataId(), outputDataSignal.m_nodeType, outputDataSignal, outputDataSignal.m_endpoint.GetNamedNodeId());
m_lastAggregateItemMap[outputDataSignal] = treeItem;
}
ScriptCanvas::Timestamp relativeTimeStamp = outputDataSignal.GetTimestamp() - m_anchorTimeStamp;
AZStd::string valueString = outputDataSignal.m_outputValue.m_datum.ToString();
treeItem->RegisterDataOutput(outputDataSignal.m_endpoint.GetSlotId(), outputDataSignal.m_endpoint.GetSlotName(), valueString, GetTreeRoot()->GetUpdatePolicy() == DebugLogRootItem::UpdatePolicy::RealTime);
}
void LoggingDataAggregator::ProcessAnnotateNode(const ScriptCanvas::AnnotateNodeSignal& annotateNodeSignal)
{
auto lastAggregateIter = m_lastAggregateItemMap.find(annotateNodeSignal);
if (lastAggregateIter != m_lastAggregateItemMap.end())
{
ExecutionLogTreeItem* treeItem = lastAggregateIter->second;
treeItem->RegisterAnnotation(annotateNodeSignal, GetTreeRoot()->GetUpdatePolicy() == DebugLogRootItem::UpdatePolicy::RealTime);
}
}
void LoggingDataAggregator::ProcessVariableChangedSignal([[maybe_unused]] const ScriptCanvas::VariableChange& variableChangeSignal)
{
}
DebugLogRootItem* LoggingDataAggregator::GetTreeRoot() const
{
return m_debugLogRoot;
}
void LoggingDataAggregator::RegisterEntityName(const AZ::EntityId& entityId, AZStd::string_view entityName)
{
auto nameIter = m_entityNameCache.find(entityId);
if (nameIter == m_entityNameCache.end())
{
m_entityNameCache[entityId] = entityName;
}
}
void LoggingDataAggregator::UnregisterEntityName(const AZ::EntityId& entityId)
{
// While we are capturing, we never want to update this list.
if (!IsCapturingData())
{
m_entityNameCache.erase(entityId);
}
}
void LoggingDataAggregator::OnRegistrationEnabled(const AZ::NamedEntityId&, const ScriptCanvas::GraphIdentifier&)
{
}
void LoggingDataAggregator::OnRegistrationDisabled(const AZ::NamedEntityId&, const ScriptCanvas::GraphIdentifier&)
{
}
void LoggingDataAggregator::ResetLog()
{
m_debugLogRoot->ResetData();
}
void LoggingDataAggregator::ResetData()
{
m_endpointData.clear();
m_variableData.clear();
for (const auto& registrationPair : m_registrationMap)
{
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEntityGraphUnregistered, registrationPair.first, registrationPair.second);
}
m_registrationMap.clear();
// Entity registrations are all transient. We need to clear them when we reset data.
// The assets should be static, so we can persist them.
m_loggingEntityMapping.clear();
m_lastAggregateItemMap.clear();
m_lastExecutionThreadMap.clear();
if (!IsCapturingData())
{
m_entityNameCache.clear();
}
m_hasAnchor = false;
m_anchorTimeStamp = ScriptCanvas::Timestamp(0);
}
void LoggingDataAggregator::RegisterScriptCanvas(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
bool foundMatch = false;
auto matchedRange = m_registrationMap.equal_range(entityId);
for (auto mapIter = matchedRange.first; mapIter != matchedRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
foundMatch = true;
AZ_Error("ScriptCanvas", false, "Received a duplicated registration callback.");
}
}
if (!foundMatch)
{
m_registrationMap.insert(AZStd::make_pair(entityId, graphIdentifier));
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEntityGraphRegistered, entityId, graphIdentifier);
}
}
void LoggingDataAggregator::UnregisterScriptCanvas(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
bool foundMatch = false;
{
auto matchedRange = m_registrationMap.equal_range(entityId);
for (auto mapIter = matchedRange.first; mapIter != matchedRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
foundMatch = true;
m_registrationMap.erase(mapIter);
break;
}
}
}
{
auto matchedRange = m_loggingEntityMapping.equal_range(entityId);
for (auto mapIter = matchedRange.first; mapIter != matchedRange.second; ++mapIter)
{
if (mapIter->second == graphIdentifier)
{
m_loggingEntityMapping.erase(mapIter);
break;
}
}
}
if (foundMatch)
{
LoggingDataNotificationBus::Event(GetDataId(), &LoggingDataNotifications::OnEntityGraphUnregistered, entityId, graphIdentifier);
}
}
}
@@ -0,0 +1,165 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/EntityUtils.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <ScriptCanvas/Core/Endpoint.h>
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
#include <ScriptCanvas/Variable/VariableCore.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingTypes.h>
namespace ScriptCanvasEditor
{
class LoggingDataAggregator;
class LoggingDataRequests
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
using BusIdType = LoggingDataId;
virtual bool IsCapturingData() const = 0;
// Return the object to allow for certain large data elements to be passed by reference instead of by value.
virtual const LoggingDataAggregator* FindLoggingData() const = 0;
virtual void EnableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) = 0;
virtual void DisableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) = 0;
virtual AZ::NamedEntityId FindNamedEntityId(const AZ::EntityId& entityId) = 0;
};
using LoggingDataRequestBus = AZ::EBus<LoggingDataRequests>;
class LoggingDataNotifications
: public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
using BusIdType = LoggingDataId;
virtual void OnDataCaptureBegin() {};
virtual void OnDataCaptureEnd() {};
virtual void OnEntityGraphRegistered([[maybe_unused]] const AZ::NamedEntityId& entityId, [[maybe_unused]] const ScriptCanvas::GraphIdentifier& assetId) {};
virtual void OnEntityGraphUnregistered([[maybe_unused]] const AZ::NamedEntityId& entityId, [[maybe_unused]] const ScriptCanvas::GraphIdentifier& assetId) {}
virtual void OnEnabledStateChanged([[maybe_unused]] bool isEnabled, [[maybe_unused]] const AZ::NamedEntityId& namedEntityId, [[maybe_unused]] const ScriptCanvas::GraphIdentifier& graphIdentifier) {}
// TODO: Find a better spot for this
virtual void OnTreeItemAdded() {}
};
using LoggingDataNotificationBus = AZ::EBus<LoggingDataNotifications>;
// Container class for all of the local elements
class LoggingDataAggregator
: public LoggingDataRequestBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(LoggingDataAggregator, AZ::SystemAllocator,0);
LoggingDataAggregator();
~LoggingDataAggregator();
const LoggingDataId& GetDataId() const;
// LoggedDataRequests
const LoggingDataAggregator* FindLoggingData() const override;
void EnableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void DisableRegistration(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
AZ::NamedEntityId FindNamedEntityId(const AZ::EntityId& entityId) override;
////
virtual bool IsCapturingData() const = 0;
virtual bool CanCaptureData() const = 0;
// Should be bus methods, but don't want to copy data
const EntityGraphRegistrationMap& GetEntityGraphRegistrationMap() const;
const LoggingEntityMap& GetLoggingEntityMap() const;
const LoggingAssetSet& GetLoggingAssetSet() const;
////
//
void ProcessSignal(const ScriptCanvas::Signal& signal);
void ProcessNodeStateChanged(const ScriptCanvas::NodeStateChange& stateChangeSignal);
void ProcessInputSignal(const ScriptCanvas::InputSignal& inputSignal);
void ProcessOutputSignal(const ScriptCanvas::OutputSignal& outputSignal);
void ProcessOutputDataSignal(const ScriptCanvas::OutputDataSignal& outputDataSignal);
void ProcessAnnotateNode(const ScriptCanvas::AnnotateNodeSignal& annotateNodeSignal);
void ProcessVariableChangedSignal(const ScriptCanvas::VariableChange& variableChangeSignal);
////
DebugLogRootItem* GetTreeRoot() const;
void RegisterEntityName(const AZ::EntityId& entityId, AZStd::string_view entityName);
void UnregisterEntityName(const AZ::EntityId& entityId);
protected:
// Methods here for child elements to do something with the data.
virtual void OnRegistrationEnabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
virtual void OnRegistrationDisabled(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void ResetData();
void ResetLog();
void RegisterScriptCanvas(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void UnregisterScriptCanvas(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
// Parsed Data Information
//
// Debug Context Information
//
// Will be used for visually displaying the data once we get to it.
AZStd::unordered_map< ScriptCanvas::Endpoint, AZStd::string > m_endpointData;
AZStd::unordered_map< ScriptCanvas::VariableId, AZStd::string > m_variableData;
////
AZStd::unordered_map< AZ::EntityId, AZStd::string > m_entityNameCache;
AZStd::unordered_map< ScriptCanvas::GraphInfo, ExecutionLogTreeItem* > m_lastAggregateItemMap;
AZStd::unordered_map< ScriptCanvas::GraphInfo, AZStd::vector<ExecutionIdentifier>> m_lastExecutionThreadMap;
private:
DebugLogRootItem* m_debugLogRoot;
// State Information
LoggingDataId m_id;
bool m_ignoreRegistrations;
bool m_hasAnchor;
ScriptCanvas::Timestamp m_anchorTimeStamp;
// TODO: Consider wrapping the three of these up into a single struct.
EntityGraphRegistrationMap m_registrationMap;
LoggingEntityMap m_loggingEntityMapping;
LoggingAssetSet m_loggedAssetSet;
};
}
@@ -0,0 +1,18 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <Editor/View/Widgets/LoggingPanel/LoggingTypes.h>
namespace ScriptCanvasEditor
{
}
@@ -0,0 +1,34 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/EntityId.h>
#include <AzCore/std/hash.h>
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
namespace ScriptCanvasEditor
{
struct ExecutionIdentifier
{
ExecutionIdentifier() = default;
};
constexpr AZ::ComponentId k_dynamicallySpawnedControllerId = static_cast<AZ::ComponentId>(-1);
typedef AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier> EntityGraphRegistrationMap;
typedef AZStd::unordered_multimap<AZ::NamedEntityId, ScriptCanvas::GraphIdentifier> LoggingEntityMap;
typedef AZStd::unordered_set<ScriptCanvas::GraphIdentifier> LoggingAssetSet;
typedef AZ::EntityId LoggingDataId;
}
@@ -0,0 +1,96 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <QMenu>
#include <QAction>
#include <AzQtComponents/Components/Widgets/SegmentBar.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindow.h>
#include <Editor/View/Widgets/LoggingPanel/ui_LoggingWindow.h>
namespace ScriptCanvasEditor
{
//////////////////
// LoggingWindow
//////////////////
LoggingWindow::LoggingWindow(QWidget* parentWidget)
: AzQtComponents::StyledDockWidget(parentWidget)
, m_ui(new Ui::LoggingWindow)
{
m_ui->setupUi(this);
// Hack to hide the close button on the first tab. Since we always want it open.
m_ui->tabWidget->setTabsClosable(true);
m_ui->tabWidget->tabBar()->setTabButton(0, QTabBar::ButtonPosition::RightSide, nullptr);
m_ui->tabWidget->tabBar()->setTabButton(0, QTabBar::ButtonPosition::LeftSide, nullptr);
m_ui->segmentWidget->addTab(new QWidget(m_ui->segmentWidget), QStringLiteral("Entities"));
m_ui->segmentWidget->addTab(new QWidget(m_ui->segmentWidget), QStringLiteral("Graphs"));
connect(m_ui->segmentWidget, &AzQtComponents::SegmentControl::currentChanged, [this](int newIndex) {
m_ui->stackedWidget->setCurrentIndex(newIndex);
});
QObject::connect(m_ui->tabWidget, &QTabWidget::currentChanged, this, &LoggingWindow::OnActiveTabChanged);
AzQtComponents::TabWidget::applySecondaryStyle(m_ui->tabWidget, false);
m_entityPageIndex = m_ui->stackedWidget->indexOf(m_ui->entitiesPage);
m_graphPageIndex = m_ui->stackedWidget->indexOf(m_ui->graphsPage);
OnActiveTabChanged(m_ui->tabWidget->currentIndex());
PivotOnEntities();
}
LoggingWindow::~LoggingWindow()
{
}
void LoggingWindow::OnActiveTabChanged([[maybe_unused]] int index)
{
LoggingWindowSession* windowSession = qobject_cast<LoggingWindowSession*>(m_ui->tabWidget->currentWidget());
if (windowSession)
{
m_activeDataId = windowSession->GetDataId();
}
m_ui->entityPivotWidget->SwitchDataSource(m_activeDataId);
m_ui->graphPivotWidget->SwitchDataSource(m_activeDataId);
}
void LoggingWindow::PivotOnEntities()
{
m_ui->stackedWidget->setCurrentIndex(m_ui->stackedWidget->indexOf(m_ui->entitiesPage));
}
void LoggingWindow::PivotOnGraphs()
{
m_ui->stackedWidget->setCurrentIndex(m_ui->stackedWidget->indexOf(m_ui->graphsPage));
}
PivotTreeWidget* LoggingWindow::GetActivePivotWidget() const
{
if (m_ui->stackedWidget->currentIndex() == m_entityPageIndex)
{
return m_ui->entityPivotWidget;
}
return nullptr;
}
#include <Editor/View/Widgets/LoggingPanel/moc_LoggingWindow.cpp>
}
@@ -0,0 +1,66 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <QButtonGroup>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingDataAggregator.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#endif
namespace Ui
{
class LoggingWindow;
}
namespace ScriptCanvasEditor
{
class PivotTreeWidget;
class LoggingWindow
: public AzQtComponents::StyledDockWidget
{
Q_OBJECT;
public:
AZ_CLASS_ALLOCATOR(LoggingWindow, AZ::SystemAllocator, 0);
LoggingWindow(QWidget* parentWidget = nullptr);
virtual ~LoggingWindow();
protected:
void OnActiveTabChanged(int index);
void PivotOnEntities();
void PivotOnGraphs();
private:
PivotTreeWidget* GetActivePivotWidget() const;
AZStd::unique_ptr<Ui::LoggingWindow> m_ui;
QButtonGroup m_pivotGroup;
LoggingDataId m_activeDataId;
int m_entityPageIndex;
int m_graphPageIndex;
};
}
@@ -0,0 +1,354 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoggingWindow</class>
<widget class="QDockWidget" name="LoggingWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>603</width>
<height>316</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="windowTitle">
<string>Debugger</string>
</property>
<widget class="QWidget" name="center">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>603</width>
<height>294</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>35</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::SegmentControl" name="segmentWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="topMargin" stdset="0">
<number>0</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>35</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="entitiesPage">
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="ScriptCanvasEditor::EntityPivotTreeWidget" name="entityPivotWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="graphsPage">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="ScriptCanvasEditor::GraphPivotTreeWidget" name="graphPivotWidget" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>4</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="AzQtComponents::TabWidget" name="tabWidget">
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="tabsClosable">
<bool>true</bool>
</property>
<property name="movable">
<bool>false</bool>
</property>
<widget class="ScriptCanvasEditor::LiveLoggingWindowSession" name="emptyCapture">
<attribute name="title">
<string>Live</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ScriptCanvasEditor::EntityPivotTreeWidget</class>
<extends>QWidget</extends>
<header location="global">Editor/View/Widgets/LoggingPanel/PivotTree/EntityPivotTree/EntityPivotTree.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ScriptCanvasEditor::LiveLoggingWindowSession</class>
<extends>QWidget</extends>
<header location="global">Editor/View/Widgets/LoggingPanel/LiveWindowSession/LiveLoggingWindowSession.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ScriptCanvasEditor::GraphPivotTreeWidget</class>
<extends>QWidget</extends>
<header location="global">Editor/View/Widgets/LoggingPanel/PivotTree/GraphPivotTree/GraphPivotTree.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::TabWidget</class>
<extends>QTabWidget</extends>
<header location="global">AzQtComponents/Components/Widgets/TabWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzQtComponents::SegmentControl</class>
<extends>QFrame</extends>
<header location="global">AzQtComponents/Components/Widgets/SegmentControl.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,483 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <QScrollBar>
#include <QGraphicsItem>
#include <QScopedValueRollback>
#include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
#include <GraphCanvas/Components/SceneBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/VisualBus.h>
#include <GraphCanvas/Widgets/StyledItemDelegates/IconDecoratedNameDelegate.h>
#include <GraphCanvas/Utils/GraphUtils.h>
#include <Editor/View/Widgets/AssetGraphSceneDataBus.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowSession.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/GraphCanvas/MappingBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
namespace ScriptCanvasEditor
{
/////////////////////////////
// LoggingWindowFilterModel
/////////////////////////////
bool LoggingWindowFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
if (m_logFilter.IsEmpty())
{
return true;
}
QAbstractItemModel* model = sourceModel();
QModelIndex index = model->index(sourceRow, 0, sourceParent);
DebugLogTreeItem* treeItem = static_cast<DebugLogTreeItem*>(index.internalPointer());
if (treeItem)
{
return treeItem->MatchesFilter(m_logFilter);
}
return false;
}
void LoggingWindowFilterModel::SetFilter(const QString& filter)
{
m_filter = filter;
m_logFilter.m_filter = QRegExp(m_filter, Qt::CaseInsensitive);
invalidateFilter();
}
void LoggingWindowFilterModel::ClearFilter()
{
SetFilter("");
}
bool LoggingWindowFilterModel::HasFilter() const
{
return !m_filter.isEmpty();
}
/////////////////////////
// LoggingWindowSession
/////////////////////////
LoggingWindowSession::LoggingWindowSession(QWidget* parentWidget)
: QWidget(parentWidget)
, m_ui(new Ui::LoggingWindowSession())
, m_clearSelectionOnSceneSelectionChange(true)
, m_scrollToBottom(true)
, m_debugRoot(nullptr)
, m_treeModel(nullptr)
, m_filterModel(nullptr)
{
m_ui->setupUi(this);
QObject::connect(m_ui->captureButton, &QToolButton::clicked, this, &LoggingWindowSession::OnCaptureButtonPressed);
QObject::connect(m_ui->expandAll, &QToolButton::clicked, this, &LoggingWindowSession::OnExpandAll);
QObject::connect(m_ui->collapseAll, &QToolButton::clicked, this, &LoggingWindowSession::OnCollapseAll);
QObject::connect(m_ui->targetSelector, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &LoggingWindowSession::OnTargetChanged);
QObject::connect(m_ui->logTree->verticalScrollBar(), &QScrollBar::valueChanged, this, &LoggingWindowSession::OnLogScrolled);
QObject::connect(m_ui->logTree, &QTreeView::expanded, this, &LoggingWindowSession::OnLogItemExpanded);
QObject::connect(m_ui->logTree->verticalScrollBar(), &QScrollBar::rangeChanged, this, &LoggingWindowSession::OnLogRangeChanged);
QObject::connect(m_ui->filterWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, this, &LoggingWindowSession::OnSearchFilterChanged);
m_ui->filterWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
m_ui->logTree->setMouseTracking(true);
QObject::connect(m_ui->logTree, &QTreeView::clicked, this, &LoggingWindowSession::OnLogClicked);
QObject::connect(m_ui->logTree, &QTreeView::doubleClicked, this, &LoggingWindowSession::OnLogDoubleClicked);
m_focusDelayTimer.setInterval(125);
m_focusDelayTimer.setSingleShot(true);
QObject::connect(&m_focusDelayTimer, &QTimer::timeout, this, &LoggingWindowSession::HandleQueuedFocus);
GraphCanvas::AssetEditorNotificationBus::Handler::BusConnect(ScriptCanvasEditor::AssetEditorId);
AZ::EntityId graphCanvasId;
GeneralRequestBus::BroadcastResult(graphCanvasId, &GeneralRequests::GetActiveGraphCanvasGraphId);
OnActiveGraphChanged(graphCanvasId);
}
LoggingWindowSession::~LoggingWindowSession()
{
}
const LoggingDataId& LoggingWindowSession::GetDataId() const
{
return m_loggingDataId;
}
void LoggingWindowSession::ClearFilter()
{
m_ui->filterWidget->ClearTextFilter();
}
void LoggingWindowSession::OnActiveGraphChanged(const AZ::EntityId& graphId)
{
ClearLoggingSelection();
if (GraphCanvas::SceneNotificationBus::Handler::BusIsConnected())
{
GraphCanvas::SceneNotificationBus::Handler::BusDisconnect();
}
if (graphId.IsValid())
{
GraphCanvas::SceneNotificationBus::Handler::BusConnect(graphId);
}
}
void LoggingWindowSession::OnSelectionChanged()
{
ClearLoggingSelection();
}
void LoggingWindowSession::RegisterTreeRoot(DebugLogRootItem* debugRoot)
{
m_debugRoot = debugRoot;
m_treeModel = aznew GraphCanvas::GraphCanvasTreeModel(debugRoot, this);
m_filterModel = aznew LoggingWindowFilterModel();
m_filterModel->setSourceModel(m_treeModel);
m_ui->logTree->setModel(m_filterModel);
m_ui->logTree->header()->setStretchLastSection(false);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::NodeName, QHeaderView::ResizeMode::Stretch);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::Input, QHeaderView::ResizeMode::Stretch);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::Output, QHeaderView::ResizeMode::Stretch);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::TimeStep, QHeaderView::ResizeMode::Fixed);
m_ui->logTree->header()->resizeSection(DebugLogTreeItem::Column::TimeStep, 75);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::ScriptName, QHeaderView::ResizeMode::Fixed);
m_ui->logTree->header()->resizeSection(DebugLogTreeItem::Column::ScriptName, 150);
m_ui->logTree->header()->setSectionResizeMode(DebugLogTreeItem::Column::SourceEntity, QHeaderView::ResizeMode::Fixed);
m_ui->logTree->header()->resizeSection(DebugLogTreeItem::Column::SourceEntity, 200);
m_ui->logTree->setItemDelegateForColumn(DebugLogTreeItem::Column::NodeName, aznew GraphCanvas::IconDecoratedNameDelegate(this));
QObject::connect(m_ui->logTree->selectionModel(), &QItemSelectionModel::selectionChanged, this, &LoggingWindowSession::OnLogSelectionChanged);
}
void LoggingWindowSession::SetDataId(const LoggingDataId& loggingDataId)
{
if (!m_loggingDataId.IsValid())
{
m_loggingDataId = loggingDataId;
}
}
void LoggingWindowSession::OnExpandAll()
{
m_ui->logTree->expandAll();
ScrollToSelection();
}
void LoggingWindowSession::OnCollapseAll()
{
m_ui->logTree->collapseAll();
ScrollToSelection();
}
void LoggingWindowSession::OnSearchFilterChanged(const QString& filterString)
{
m_filterModel->SetFilter(filterString);
}
void LoggingWindowSession::OnLogScrolled(int value)
{
if (m_ui->logTree->verticalScrollBar()->isEnabled())
{
if (m_ui->logTree->verticalScrollBar()->maximum() == value)
{
m_scrollToBottom = true;
}
else
{
m_scrollToBottom = false;
}
}
else
{
m_scrollToBottom = true;
}
}
void LoggingWindowSession::OnLogItemExpanded([[maybe_unused]] const QModelIndex& modelIndex)
{
m_scrollToBottom = false;
}
void LoggingWindowSession::OnLogRangeChanged([[maybe_unused]] int min, int max)
{
if (m_scrollToBottom)
{
m_ui->logTree->verticalScrollBar()->setValue(max);
}
if (!m_ui->logTree->verticalScrollBar()->isEnabled())
{
m_scrollToBottom = true;
}
}
void LoggingWindowSession::OnLogClicked(const QModelIndex& modelIndex)
{
if (modelIndex.column() == DebugLogTreeItem::Column::ScriptName)
{
QModelIndex sourceIndex = m_filterModel->mapToSource(modelIndex);
DebugLogTreeItem* treeItem = static_cast<DebugLogTreeItem*>(sourceIndex.internalPointer());
if (ExecutionLogTreeItem* executionItem = azrtti_cast<ExecutionLogTreeItem*>(treeItem))
{
QScopedValueRollback<bool> valueRollback(m_clearSelectionOnSceneSelectionChange, false);
const AZ::Data::AssetId& assetId = executionItem->GetAssetId();
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAssetId, assetId);
}
}
}
void LoggingWindowSession::OnLogDoubleClicked(const QModelIndex& modelIndex)
{
ExecutionLogTreeItem* executionItem = ResolveExecutionItem(modelIndex);
if (executionItem)
{
const AZ::Data::AssetId& assetId = executionItem->GetAssetId();
bool isAssetOpen = false;
GeneralRequestBus::BroadcastResult(isAssetOpen, &GeneralRequests::IsScriptCanvasAssetOpen, assetId);
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAssetId, assetId);
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, assetId);
if (isAssetOpen)
{
FocusOnElement(assetId, executionItem->GetScriptCanvasAssetNodeId());
}
else
{
m_assetId = assetId;
m_assetNodeId = executionItem->GetScriptCanvasAssetNodeId();
m_focusDelayTimer.stop();
m_focusDelayTimer.start();
}
}
}
void LoggingWindowSession::OnLogSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
for (const QModelIndex& deselectedIndex : deselected.indexes())
{
if (deselectedIndex.column() != 0)
{
continue;
}
ExecutionLogTreeItem* executionItem = ResolveExecutionItem(deselectedIndex);
if (executionItem)
{
RemoveHighlight(executionItem->GetAssetId(), executionItem->GetScriptCanvasAssetNodeId());
}
}
for (const QModelIndex& selectedIndex : selected.indexes())
{
if (selectedIndex.column() != 0)
{
continue;
}
ExecutionLogTreeItem* executionItem = ResolveExecutionItem(selectedIndex);
if (executionItem)
{
HighlightElement(executionItem->GetAssetId(), executionItem->GetScriptCanvasAssetNodeId());
}
}
}
ExecutionLogTreeItem* LoggingWindowSession::ResolveExecutionItem(const QModelIndex& proxyModelIndex)
{
QModelIndex sourceIndex = m_filterModel->mapToSource(proxyModelIndex);
DebugLogTreeItem* treeItem = static_cast<DebugLogTreeItem*>(sourceIndex.internalPointer());
DebugLogTreeItem* parentItem = static_cast<DebugLogTreeItem*>(treeItem->GetParent());
ExecutionLogTreeItem* executionItem = azrtti_cast<ExecutionLogTreeItem*>(treeItem);
while (executionItem == nullptr && parentItem != nullptr)
{
executionItem = azrtti_cast<ExecutionLogTreeItem*>(parentItem);
parentItem = static_cast<DebugLogTreeItem*>(parentItem->GetParent());
}
return executionItem;
}
void LoggingWindowSession::HandleQueuedFocus()
{
AZ::EntityId activeGraphCanvasGraphId;
GeneralRequestBus::BroadcastResult(activeGraphCanvasGraphId, &GeneralRequests::GetActiveGraphCanvasGraphId);
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, m_assetId);
if (activeGraphCanvasGraphId == graphCanvasGraphId)
{
FocusOnElement(m_assetId, m_assetNodeId);
m_focusDelayTimer.stop();
m_assetId.SetInvalid();
m_assetNodeId.SetInvalid();
}
}
void LoggingWindowSession::FocusOnElement(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId)
{
GraphCanvas::FocusConfig focusConfig;
AZ::EntityId scriptCanvasNodeId;
AssetGraphSceneBus::BroadcastResult(scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, assetId, assetNodeId);
GraphCanvas::NodeId graphCanvasNodeId;
SceneMemberMappingRequestBus::EventResult(graphCanvasNodeId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
if (GraphCanvas::GraphUtils::IsNodeGroup(graphCanvasNodeId))
{
focusConfig.m_spacingType = GraphCanvas::FocusConfig::SpacingType::GridStep;
focusConfig.m_spacingAmount = 1;
}
else
{
focusConfig.m_spacingType = GraphCanvas::FocusConfig::SpacingType::Scalar;
focusConfig.m_spacingAmount = 2;
}
AZStd::vector< AZ::EntityId > memberIds = { graphCanvasNodeId };
GraphCanvas::GraphUtils::FocusOnElements(memberIds, focusConfig);
{
QScopedValueRollback<bool> maintainSelection(m_clearSelectionOnSceneSelectionChange, false);
RemoveHighlight(assetId, assetNodeId);
GraphCanvas::GraphId graphId;
GraphCanvas::SceneMemberRequestBus::EventResult(graphId, graphCanvasNodeId, &GraphCanvas::SceneMemberRequests::GetScene);
GraphCanvas::SceneRequestBus::Event(graphId, &GraphCanvas::SceneRequests::ClearSelection);
GraphCanvas::SceneMemberUIRequestBus::Event(graphCanvasNodeId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
}
}
void LoggingWindowSession::HighlightElement(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId)
{
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, assetId);
if (graphCanvasGraphId.IsValid())
{
AZ::EntityId scriptCanvasNodeId;
AssetGraphSceneBus::BroadcastResult(scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, assetId, assetNodeId);
GraphCanvas::NodeId graphCanvasNodeId;
SceneMemberMappingRequestBus::EventResult(graphCanvasNodeId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
GraphCanvas::SceneMemberGlowOutlineConfiguration glowConfiguration;
glowConfiguration.m_sceneMember = graphCanvasNodeId;
glowConfiguration.m_blurRadius = 5;
glowConfiguration.m_pen = QPen();
glowConfiguration.m_pen.setBrush(QColor(243, 129, 29));
glowConfiguration.m_pen.setWidth(5);
glowConfiguration.m_pulseRate = AZStd::chrono::milliseconds(2500);
glowConfiguration.m_zValue = 0;
GraphCanvas::GraphicsEffectId effectId;
GraphCanvas::SceneRequestBus::EventResult(effectId, graphCanvasGraphId, &GraphCanvas::SceneRequests::CreateGlowOnSceneMember, glowConfiguration);
auto effectIter = m_highlightEffects.find(assetNodeId);
if (effectIter != m_highlightEffects.end())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::CancelGraphicsEffect, effectIter->second);
}
m_highlightEffects[assetNodeId] = effectId;
}
}
void LoggingWindowSession::RemoveHighlight(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId)
{
auto effectIter = m_highlightEffects.find(assetNodeId);
if (effectIter != m_highlightEffects.end())
{
AZ::EntityId graphCanvasGraphId;
GeneralRequestBus::BroadcastResult(graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, assetId);
if (graphCanvasGraphId.IsValid())
{
GraphCanvas::SceneRequestBus::Event(graphCanvasGraphId, &GraphCanvas::SceneRequests::CancelGraphicsEffect, effectIter->second);
}
m_highlightEffects.erase(effectIter);
}
}
void LoggingWindowSession::ScrollToSelection()
{
for (auto selectedIndex : m_ui->logTree->selectionModel()->selectedIndexes())
{
m_ui->logTree->scrollTo(selectedIndex);
}
}
void LoggingWindowSession::ClearLoggingSelection()
{
if (m_clearSelectionOnSceneSelectionChange)
{
m_ui->logTree->clearSelection();
}
}
#include <Editor/View/Widgets/LoggingPanel/moc_LoggingWindowSession.cpp>
}
@@ -0,0 +1,148 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
// qbrush.h(118): warning C4251: 'QBrush::d': class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
// qwidget.h(858): warning C4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#if !defined(Q_MOC_RUN)
#include <QAbstractItemModel>
#include <QIcon>
#include <QSortFilterProxyModel>
#include <QWidget>
AZ_POP_DISABLE_WARNING
#include <AzCore/Component/NamedEntityId.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Editor/AssetEditorBus.h>
#include <GraphCanvas/Styling/StyleHelper.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeModel.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h>
#include <ScriptCanvas/Core/Endpoint.h>
#include <ScriptCanvas/Core/NodeBus.h>
// Qt Generated
// warning C4251: 'QLayoutItem::align': class 'QFlags<Qt::AlignmentFlag>' needs to have dll-interface to be used by clients of class 'QLayoutItem'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <Editor/View/Widgets/LoggingPanel/ui_LoggingWindowSession.h>
#endif
AZ_POP_DISABLE_WARNING
namespace ScriptCanvasEditor
{
class LoggingWindowFilterModel
: public QSortFilterProxyModel
{
public:
AZ_CLASS_ALLOCATOR(LoggingWindowFilterModel, AZ::SystemAllocator, 0);
LoggingWindowFilterModel() = default;
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
void SetFilter(const QString& filter);
void ClearFilter();
bool HasFilter() const;
private:
QString m_filter;
DebugLogFilter m_logFilter;
};
class LoggingWindowSession
: public QWidget
, public GraphCanvas::AssetEditorNotificationBus::Handler
, public GraphCanvas::SceneNotificationBus::Handler
{
Q_OBJECT
protected:
LoggingWindowSession(QWidget* parentWidget = nullptr);
public:
~LoggingWindowSession() override;
const LoggingDataId& GetDataId() const;
void ClearFilter();
// GraphCanvas::AssetEditorNotificationBus
void OnActiveGraphChanged(const AZ::EntityId& graphId) override;
////
// GraphCanvas::SceneNotificationBus
void OnSelectionChanged() override;
////
protected:
void RegisterTreeRoot(DebugLogRootItem* debugRoot);
void SetDataId(const LoggingDataId& loggingDataId);
virtual void OnCaptureButtonPressed() = 0;
virtual void OnPlaybackButtonPressed() = 0;
virtual void OnOptionsButtonPressed() = 0;
virtual void OnTargetChanged(int currentIndex) = 0;
void OnExpandAll();
void OnCollapseAll();
protected:
AZStd::unique_ptr< Ui::LoggingWindowSession > m_ui;
private:
void OnSearchFilterChanged(const QString& filterString);
void OnLogScrolled(int value);
void OnLogItemExpanded(const QModelIndex& modelIndex);
void OnLogRangeChanged(int min, int max);
void OnLogClicked(const QModelIndex& modelIndex);
void OnLogDoubleClicked(const QModelIndex& modelIndex);
void OnLogSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
ExecutionLogTreeItem* ResolveExecutionItem(const QModelIndex& proxyModelIndex);
void HandleQueuedFocus();
void FocusOnElement(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId);
void HighlightElement(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId);
void RemoveHighlight(const AZ::Data::AssetId& assetId, const AZ::EntityId& assetNodeId);
void ScrollToSelection();
void ClearLoggingSelection();
bool m_clearSelectionOnSceneSelectionChange;
bool m_scrollToBottom;
LoggingDataId m_loggingDataId;
DebugLogRootItem* m_debugRoot;
GraphCanvas::GraphCanvasTreeModel* m_treeModel;
LoggingWindowFilterModel* m_filterModel;
AZStd::unordered_map< AZ::EntityId, GraphCanvas::GraphicsEffectId > m_highlightEffects;
QTimer m_focusDelayTimer;
AZ::Data::AssetId m_assetId;
AZ::EntityId m_assetNodeId;
};
}
@@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LoggingWindowSession</class>
<widget class="QWidget" name="LoggingWindowSession">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>897</width>
<height>108</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="targetSelector">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="captureButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/capture_offline.png</normaloff>:/ScriptCanvasEditorResources/Resources/capture_offline.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="autoCaptureToggle">
<property name="toolTip">
<string>Controls whether or not capture will enable as soon as the desired target connects.</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/auto_record.png</normaloff>:/ScriptCanvasEditorResources/Resources/auto_record.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="liveUpdatesToggle">
<property name="toolTip">
<string>Controls whether or not the Logging View live updates with the Captured Data (Usually want to disable for performance reasons)</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/live_update.png</normaloff>:/ScriptCanvasEditorResources/Resources/live_update.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="expandAll">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/ExpandAll_Icon.png</normaloff>:/ScriptCanvasEditorResources/Resources/ExpandAll_Icon.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="collapseAll">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../Windows/ScriptCanvasEditorResources.qrc">
<normaloff>:/ScriptCanvasEditorResources/Resources/CollapseAll_Icon.png</normaloff>:/ScriptCanvasEditorResources/Resources/CollapseAll_Icon.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="filterWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="openIcon">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTreeView" name="logTree">
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QWidget</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../Windows/ScriptCanvasEditorResources.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,947 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
#include <GraphCanvas/Components/Slots/SlotBus.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <GraphCanvas/Utils/GraphUtils.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowTreeItems.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/Include/ScriptCanvas/GraphCanvas/NodeDescriptorBus.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#include <Editor/View/Widgets/AssetGraphSceneDataBus.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/GraphCanvas/MappingBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
namespace ScriptCanvasEditor
{
/////////////////////
// DebugLogTreeItem
/////////////////////
bool DebugLogTreeItem::MatchesFilter(const DebugLogFilter& treeFilter)
{
DebugLogTreeItem* parent = static_cast<DebugLogTreeItem*>(GetParent());
while (parent)
{
// We don't want to match against the root, since it always matches.
// So only check things that have a valid parent.
DebugLogTreeItem* nextParent = static_cast<DebugLogTreeItem*>(parent->GetParent());
if (nextParent != nullptr && parent->OnMatchesFilter(treeFilter))
{
return true;
}
parent = static_cast<DebugLogTreeItem*>(parent->GetParent());
}
DebugLogTreeItem* currentItem = this;
AZStd::unordered_set< DebugLogTreeItem* > children;
while (currentItem)
{
if (currentItem->OnMatchesFilter(treeFilter))
{
return true;
}
for (int i = 0; i < currentItem->GetChildCount(); ++i)
{
children.insert(static_cast<DebugLogTreeItem*>(currentItem->FindChildByRow(i)));
}
if (!children.empty())
{
currentItem = (*children.begin());
children.erase(children.begin());
}
else
{
currentItem = nullptr;
}
}
return false;
}
const ScriptCanvas::Endpoint& DebugLogTreeItem::GetIncitingEndpoint() const
{
return m_incitingEndpoint;
}
bool DebugLogTreeItem::IsTriggeredBy(const ScriptCanvas::Endpoint& endpoint) const
{
return m_incitingEndpoint == endpoint;
}
Qt::ItemFlags DebugLogTreeItem::Flags([[maybe_unused]] const QModelIndex& index) const
{
return Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable;
}
int DebugLogTreeItem::GetColumnCount() const
{
return Column::Count;
}
void DebugLogTreeItem::SetIncitingEndpoint(const ScriptCanvas::Endpoint& endpoint)
{
m_incitingEndpoint = endpoint;
}
/////////////////////
// DebugLogRootItem
/////////////////////
DebugLogRootItem::DebugLogRootItem()
: m_updatePolicy(UpdatePolicy::Batched)
{
m_additionTimer.setSingleShot(true);
m_additionTimer.setInterval(1000);
QObject::connect(&m_additionTimer, &QTimer::timeout, [this]() { this->RedoLayout(); });
}
DebugLogRootItem::~DebugLogRootItem()
{
}
ExecutionLogTreeItem* DebugLogRootItem::CreateExecutionItem(const LoggingDataId& loggingDataId, const ScriptCanvas::NodeTypeIdentifier& nodeType, const ScriptCanvas::GraphInfo& graphInfo, const ScriptCanvas::NamedNodeId& nodeId)
{
ExecutionLogTreeItem* treeItem = nullptr;
bool signalChanged = false;
if (m_updatePolicy == UpdatePolicy::Batched)
{
if (!m_additionTimer.isActive())
{
m_additionTimer.start();
}
}
if (m_updatePolicy == UpdatePolicy::SingleTime)
{
treeItem = CreateChildNodeWithoutAddSignal<ExecutionLogTreeItem>(loggingDataId, nodeType, graphInfo, nodeId);
}
else
{
treeItem = CreateChildNode<ExecutionLogTreeItem>(loggingDataId, nodeType, graphInfo, nodeId);
}
return treeItem;
}
QVariant DebugLogRootItem::Data([[maybe_unused]] const QModelIndex& index, [[maybe_unused]] int role) const
{
return QVariant();
}
void DebugLogRootItem::ResetData()
{
SignalLayoutAboutToBeChanged();
ClearChildren();
SignalLayoutChanged();
}
void DebugLogRootItem::SetUpdatePolicy(UpdatePolicy updatePolicy)
{
if (m_updatePolicy != updatePolicy)
{
m_updatePolicy = updatePolicy;
m_additionTimer.stop();
}
}
DebugLogRootItem::UpdatePolicy DebugLogRootItem::GetUpdatePolicy() const
{
return m_updatePolicy;
}
void DebugLogRootItem::RedoLayout()
{
m_additionTimer.stop();
SignalLayoutAboutToBeChanged();
SignalLayoutChanged();
}
/////////////////////////
// ExecutionLogTreeItem
/////////////////////////
ExecutionLogTreeItem::ExecutionLogTreeItem(const LoggingDataId& loggingDataId, const ScriptCanvas::NodeTypeIdentifier& nodeType, const ScriptCanvas::GraphInfo& graphInfo, const ScriptCanvas::NamedNodeId& nodeId)
: m_loggingDataId(loggingDataId)
, m_nodeType(nodeType)
, m_graphInfo(graphInfo)
, m_scriptCanvasAssetNodeId(nodeId)
, m_iconPixmap(nullptr)
{
m_paletteConfiguration.m_iconPalette = "NodePaletteTypeIcon";
m_paletteConfiguration.SetColorPalette("MethodNodeTitlePalette");
AZ::NamedEntityId entityName;
LoggingDataRequestBus::EventResult(entityName, m_loggingDataId, &LoggingDataRequests::FindNamedEntityId, m_graphInfo.m_runtimeEntity);
m_sourceEntityName = entityName.ToString().c_str();
m_displayName = nodeId.m_name.c_str();
ScrapeBehaviorContextData();
ScrapeGraphCanvasData();
m_inputName = "---";
m_outputName = "---";
GeneralAssetNotificationBus::Handler::BusConnect(GetAssetId());
}
QVariant ExecutionLogTreeItem::Data(const QModelIndex& index, int role) const
{
switch (index.column())
{
case Column::NodeName:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_displayName;
}
else if (role == Qt::DecorationRole)
{
if (m_iconPixmap != nullptr)
{
return (*m_iconPixmap);
}
}
}
break;
case Column::Input:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_inputName;
}
}
break;
case Column::Output:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_outputName;
}
}
break;
case Column::TimeStep:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_timeString;
}
}
break;
case Column::ScriptName:
{
if (role == Qt::DisplayRole)
{
return m_graphName;
}
else if (role == Qt::ToolTipRole)
{
return m_relativeGraphPath;
}
else if (role == Qt::ForegroundRole)
{
return QColor(42,132,252);
}
else if (role == Qt::FontRole)
{
QFont font;
font.setUnderline(true);
return font;
}
}
break;
case Column::SourceEntity:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
return m_sourceEntityName;
}
}
break;
default:
break;
}
return QVariant();
}
AZ::EntityId ExecutionLogTreeItem::GetNodeId() const
{
return m_scriptCanvasAssetNodeId;
}
void ExecutionLogTreeItem::RegisterAnnotation(const ScriptCanvas::AnnotateNodeSignal& annotationSignal, bool allowAddSignal)
{
// The QTreeView does have a setFirstColumnSpanned, but it doesn't seem dynamic, nor model driven.
// So I don't want to use it.
if (allowAddSignal)
{
CreateChildNode<NodeAnnotationTreeItem>(annotationSignal.m_annotationLevel, annotationSignal.m_annotation);
}
else
{
CreateChildNodeWithoutAddSignal<NodeAnnotationTreeItem>(annotationSignal.m_annotationLevel, annotationSignal.m_annotation);
}
}
void ExecutionLogTreeItem::RegisterDataInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::string_view dataString, bool allowAddSignal)
{
if (!HasExecutionInput() && !HasExecutionOutput())
{
ResolveWrapperNode();
}
DataLogTreeItem* dataTreeItem = nullptr;
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* testLogItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (testLogItem && !testLogItem->HasInput())
{
dataTreeItem = testLogItem;
break;
}
}
if (dataTreeItem == nullptr)
{
if (allowAddSignal)
{
dataTreeItem = CreateChildNode<DataLogTreeItem>(GetGraphIdentifier());
}
else
{
dataTreeItem = CreateChildNodeWithoutAddSignal<DataLogTreeItem>(GetGraphIdentifier());
}
}
ScriptCanvas::Endpoint endpoint(m_scriptCanvasAssetNodeId, slotId);
dataTreeItem->RegisterDataInput(incitingEndpoint, endpoint, slotName, dataString);
}
void ExecutionLogTreeItem::RegisterDataOutput(const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::string_view dataString, bool allowAddSignal)
{
if (!HasExecutionInput() && !HasExecutionOutput())
{
ResolveWrapperNode();
}
DataLogTreeItem* dataTreeItem = nullptr;
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* testLogItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (testLogItem && !testLogItem->HasOutput())
{
dataTreeItem = testLogItem;
break;
}
}
if (dataTreeItem == nullptr)
{
if (allowAddSignal)
{
dataTreeItem = CreateChildNode<DataLogTreeItem>(GetGraphIdentifier());
}
else
{
dataTreeItem = CreateChildNodeWithoutAddSignal<DataLogTreeItem>(GetGraphIdentifier());
}
}
ScriptCanvas::Endpoint endpoint(m_scriptCanvasAssetNodeId, slotId);
dataTreeItem->RegisterDataOutput(endpoint, slotName, dataString);
}
void ExecutionLogTreeItem::RegisterExecutionInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::chrono::milliseconds relativeExecution)
{
m_timeString = QTime::fromMSecsSinceStartOfDay(aznumeric_cast<int>(relativeExecution.count())).toString("mm:ss.zzz");
m_inputSlot = slotId;
m_inputName = slotName.data();
SetIncitingEndpoint(incitingEndpoint);
if (!HasExecutionOutput())
{
ResolveWrapperNode();
}
PopulateInputSlotData();
SignalDataChanged();
}
bool ExecutionLogTreeItem::HasExecutionInput() const
{
return m_inputSlot.IsValid();
}
void ExecutionLogTreeItem::RegisterExecutionOutput(const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::chrono::milliseconds relativeExecution)
{
if (!HasExecutionInput())
{
m_timeString = QTime::fromMSecsSinceStartOfDay(aznumeric_cast<int>(relativeExecution.count())).toString("mm:ss.zzz");
}
m_outputSlot = slotId;
m_outputName = slotName.data();
if (!HasExecutionInput())
{
ResolveWrapperNode();
}
PopulateOutputSlotData();
SignalDataChanged();
}
bool ExecutionLogTreeItem::HasExecutionOutput() const
{
return m_outputSlot.IsValid();
}
void ExecutionLogTreeItem::OnStylesUnloaded()
{
m_iconPixmap = nullptr;
}
void ExecutionLogTreeItem::OnStylesLoaded()
{
GraphCanvas::StyleManagerRequestBus::EventResult(m_iconPixmap, ScriptCanvasEditor::AssetEditorId, &GraphCanvas::StyleManagerRequests::GetConfiguredPaletteIcon, m_paletteConfiguration);
SignalDataChanged();
}
void ExecutionLogTreeItem::OnAssetVisualized()
{
ScrapeGraphCanvasData();
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* dataLogTreeItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (dataLogTreeItem)
{
dataLogTreeItem->ScrapeData();
}
}
}
void ExecutionLogTreeItem::OnAssetUnloaded()
{
EditorGraphNotificationBus::Handler::BusDisconnect();
m_scriptCanvasNodeId.SetInvalid();
m_graphCanvasGraphId.SetInvalid();
m_graphCanvasNodeId.SetInvalid();
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* dataLogTreeItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (dataLogTreeItem)
{
dataLogTreeItem->InvalidateEditorIds();
}
}
}
void ExecutionLogTreeItem::OnGraphCanvasSceneDisplayed()
{
m_graphCanvasGraphId.SetInvalid();
m_graphCanvasNodeId.SetInvalid();
for (int i = 0; i < GetChildCount(); ++i)
{
DataLogTreeItem* dataLogTreeItem = azrtti_cast<DataLogTreeItem*>(FindChildByRow(i));
if (dataLogTreeItem)
{
dataLogTreeItem->InvalidateGraphCanvasIds();
}
}
ScrapeGraphCanvasData();
}
const ScriptCanvas::GraphIdentifier& ExecutionLogTreeItem::GetGraphIdentifier() const
{
return m_graphInfo.m_graphIdentifier;
}
const AZ::Data::AssetId& ExecutionLogTreeItem::GetAssetId() const
{
return m_graphInfo.m_graphIdentifier.m_assetId;
}
AZ::EntityId ExecutionLogTreeItem::GetScriptCanvasAssetNodeId() const
{
return m_scriptCanvasAssetNodeId;
}
GraphCanvas::NodeId ExecutionLogTreeItem::GetGraphCanvasNodeId() const
{
return m_graphCanvasNodeId;
}
bool ExecutionLogTreeItem::OnMatchesFilter(const DebugLogFilter& treeFilter)
{
bool matches = false;
matches = matches || (m_displayName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_inputName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_outputName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_graphName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_sourceEntityName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_timeString.lastIndexOf(treeFilter.m_filter) >= 0);
return matches;
}
void ExecutionLogTreeItem::ResolveWrapperNode(bool refreshData)
{
if (m_graphCanvasNodeId.IsValid())
{
if (GraphCanvas::GraphUtils::IsWrapperNode(m_graphCanvasNodeId))
{
AZ::EntityId originalNodeId = m_graphCanvasNodeId;
ScriptCanvas::SlotId slotId;
if (HasExecutionInput())
{
slotId = m_inputSlot;
}
if (HasExecutionOutput())
{
slotId = m_outputSlot;
}
GraphCanvas::Endpoint endpoint;
EBusHandlerNodeDescriptorRequestBus::EventResult(endpoint, m_graphCanvasNodeId, &EBusHandlerNodeDescriptorRequests::MapSlotToGraphCanvasEndpoint, slotId);
if (endpoint.IsValid())
{
m_graphCanvasNodeId = endpoint.GetNodeId();
}
if (originalNodeId != m_graphCanvasNodeId && refreshData)
{
ScrapeGraphCanvasData();
}
}
}
}
void ExecutionLogTreeItem::ScrapeBehaviorContextData()
{
if (m_graphName.isEmpty())
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, GetAssetId());
AZStd::string fileName;
AzFramework::StringFunc::Path::GetFileName(assetInfo.m_relativePath.c_str(), fileName);
m_relativeGraphPath = assetInfo.m_relativePath.c_str();
m_graphName = fileName.c_str();
if (m_graphName.isEmpty())
{
m_graphName = "Unknown Canvas";
m_relativeGraphPath = GetAssetId().ToString<AZStd::string>().c_str();
}
}
const NodePaletteModelInformation* modelInformation = nullptr;
GeneralRequestBus::BroadcastResult(modelInformation, &GeneralRequests::FindNodePaletteModelInformation, m_nodeType);
if (modelInformation)
{
const CategoryInformation* categoryInformation = nullptr;
GeneralRequestBus::BroadcastResult(categoryInformation, &GeneralRequests::FindNodePaletteCategoryInformation, modelInformation->m_categoryPath);
m_displayName = QString(modelInformation->m_displayName.c_str());
if (categoryInformation && categoryInformation->m_paletteOverride.compare(GraphCanvas::NodePaletteTreeItem::DefaultNodeTitlePalette) != 0)
{
m_paletteConfiguration.SetColorPalette(categoryInformation->m_paletteOverride);
}
else if (!modelInformation->m_titlePaletteOverride.empty())
{
m_paletteConfiguration.SetColorPalette(modelInformation->m_titlePaletteOverride);
}
else
{
m_paletteConfiguration.SetColorPalette(GraphCanvas::NodePaletteTreeItem::DefaultNodeTitlePalette);
}
}
OnStylesLoaded();
SignalDataChanged();
}
void ExecutionLogTreeItem::ScrapeGraphCanvasData()
{
if (!m_graphCanvasGraphId.IsValid())
{
GeneralRequestBus::BroadcastResult(m_graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, GetAssetId());
if (!EditorGraphNotificationBus::Handler::BusIsConnected())
{
ScriptCanvas::ScriptCanvasId scriptCanvasId;
GeneralRequestBus::BroadcastResult(scriptCanvasId, &GeneralRequests::FindScriptCanvasIdByAssetId, GetAssetId());
EditorGraphNotificationBus::Handler::BusConnect(scriptCanvasId);
}
}
if (m_graphCanvasGraphId.IsValid())
{
if (!m_graphCanvasNodeId.IsValid())
{
AssetGraphSceneBus::BroadcastResult(m_scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, GetAssetId(), m_scriptCanvasAssetNodeId);
SceneMemberMappingRequestBus::EventResult(m_graphCanvasNodeId, m_scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
}
if (m_graphCanvasNodeId.IsValid())
{
const bool refreshDisplayData = false;
ResolveWrapperNode(refreshDisplayData);
AZStd::string displayName;
GraphCanvas::NodeTitleRequestBus::EventResult(displayName, m_graphCanvasNodeId, &GraphCanvas::NodeTitleRequests::GetTitle);
if (!displayName.empty())
{
m_displayName = displayName.c_str();
}
GraphCanvas::NodeTitleRequestBus::Event(m_graphCanvasNodeId, &GraphCanvas::NodeTitleRequests::ConfigureIconConfiguration, m_paletteConfiguration);
OnStylesLoaded();
PopulateInputSlotData();
PopulateOutputSlotData();
SignalDataChanged();
}
}
}
void ExecutionLogTreeItem::PopulateInputSlotData()
{
if (m_graphCanvasNodeId.IsValid() && HasExecutionInput())
{
GraphCanvas::SlotId slotId;
SlotMappingRequestBus::EventResult(slotId, m_graphCanvasNodeId, &SlotMappingRequests::MapToGraphCanvasId, m_inputSlot);
AZStd::string inputName;
GraphCanvas::SlotRequestBus::EventResult(inputName, slotId, &GraphCanvas::SlotRequests::GetName);
if (!inputName.empty())
{
m_inputName = inputName.c_str();
}
}
}
void ExecutionLogTreeItem::PopulateOutputSlotData()
{
if (m_graphCanvasNodeId.IsValid() && HasExecutionOutput())
{
GraphCanvas::SlotId slotId;
SlotMappingRequestBus::EventResult(slotId, m_graphCanvasNodeId, &SlotMappingRequests::MapToGraphCanvasId, m_outputSlot);
AZStd::string outputName;
GraphCanvas::SlotRequestBus::EventResult(outputName, slotId, &GraphCanvas::SlotRequests::GetName);
if (!outputName.empty())
{
m_outputName = outputName.c_str();
}
}
}
////////////////////
// DataLogTreeItem
////////////////////
DataLogTreeItem::DataLogTreeItem(const ScriptCanvas::GraphIdentifier& graphIdentifier)
: m_graphIdentifier(graphIdentifier)
{
m_inputName = "---";
m_outputName = "---";
ScrapeData();
}
QVariant DataLogTreeItem::Data(const QModelIndex& index, int role) const
{
switch (index.column())
{
case Column::Input:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
if (m_inputData.isEmpty())
{
return m_inputName;
}
return QString("%1 - (%2)").arg(m_inputName, m_inputData);
}
}
break;
case Column::Output:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTipRole)
{
if (m_outputData.isEmpty())
{
return m_outputName;
}
return QString("%1 - (%2)").arg(m_outputName, m_outputData);
}
}
break;
default:
break;
}
return QVariant();
}
void DataLogTreeItem::RegisterDataInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::Endpoint& endpoint, AZStd::string_view slotName, AZStd::string_view dataString)
{
SetIncitingEndpoint(incitingEndpoint);
m_assetInputEndpoint = endpoint;
m_inputName = slotName.data();
m_inputData = dataString.data();
ScrapeInputName();
}
bool DataLogTreeItem::HasInput() const
{
return m_assetInputEndpoint.IsValid();
}
void DataLogTreeItem::RegisterDataOutput(const ScriptCanvas::Endpoint& endpoint, AZStd::string_view slotName, AZStd::string_view dataString)
{
m_assetOutputEndpoint = endpoint;
m_outputName = slotName.data();
m_outputData = dataString.data();
ScrapeOutputName();
}
bool DataLogTreeItem::HasOutput() const
{
return m_assetOutputEndpoint.IsValid();
}
bool DataLogTreeItem::OnMatchesFilter(const DebugLogFilter& treeFilter)
{
bool matches = false;
matches = matches || (m_inputName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_inputData.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_outputName.lastIndexOf(treeFilter.m_filter) >= 0);
matches = matches || (m_outputData.lastIndexOf(treeFilter.m_filter) >= 0);
return matches;
}
AZ::Data::AssetId DataLogTreeItem::GetAssetId() const
{
return m_graphIdentifier.m_assetId;
}
void DataLogTreeItem::ScrapeData()
{
if (!m_graphCanvasGraphId.IsValid())
{
GeneralRequestBus::BroadcastResult(m_graphCanvasGraphId, &GeneralRequests::FindGraphCanvasGraphIdByAssetId, m_graphIdentifier.m_assetId);
}
ScrapeInputName();
ScrapeOutputName();
}
void DataLogTreeItem::InvalidateEditorIds()
{
InvalidateGraphCanvasIds();
}
void DataLogTreeItem::InvalidateGraphCanvasIds()
{
m_graphCanvasGraphId.SetInvalid();
}
void DataLogTreeItem::ScrapeInputName()
{
if (m_graphCanvasGraphId.IsValid() && m_assetInputEndpoint.IsValid())
{
AZ::EntityId scriptCanvasNodeId;
AssetGraphSceneBus::BroadcastResult(scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, GetAssetId(), m_assetInputEndpoint.GetNodeId());
GraphCanvas::NodeId graphCanvasNodeId;
SceneMemberMappingRequestBus::EventResult(graphCanvasNodeId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
GraphCanvas::SlotId slotId;
SlotMappingRequestBus::EventResult(slotId, graphCanvasNodeId, &SlotMappingRequests::MapToGraphCanvasId, m_assetInputEndpoint.GetSlotId());
AZStd::string name;
GraphCanvas::SlotRequestBus::EventResult(name, slotId, &GraphCanvas::SlotRequests::GetName);
if (!name.empty())
{
m_inputName = name.c_str();
}
}
}
void DataLogTreeItem::ScrapeOutputName()
{
if (m_graphCanvasGraphId.IsValid() && m_assetOutputEndpoint.IsValid())
{
AZ::EntityId scriptCanvasNodeId;
AssetGraphSceneBus::BroadcastResult(scriptCanvasNodeId, &AssetGraphScene::FindEditorNodeIdByAssetNodeId, GetAssetId(), m_assetOutputEndpoint.GetNodeId());
GraphCanvas::NodeId graphCanvasNodeId;
SceneMemberMappingRequestBus::EventResult(graphCanvasNodeId, scriptCanvasNodeId, &SceneMemberMappingRequests::GetGraphCanvasEntityId);
GraphCanvas::SlotId slotId;
SlotMappingRequestBus::EventResult(slotId, graphCanvasNodeId, &SlotMappingRequests::MapToGraphCanvasId, m_assetOutputEndpoint.GetSlotId());
AZStd::string name;
GraphCanvas::SlotRequestBus::EventResult(name, slotId, &GraphCanvas::SlotRequests::GetName);
if (!name.empty())
{
m_outputName = name.c_str();
}
}
}
bool DataLogTreeItem::LessThan(const GraphCanvas::GraphCanvasTreeItem* graphItem) const
{
return !azrtti_istypeof<const NodeAnnotationTreeItem*>(graphItem);
}
///////////////////////////
// NodeAnnotationTreeItem
///////////////////////////
NodeAnnotationTreeItem::NodeAnnotationTreeItem()
: m_annotationLevel(ScriptCanvas::AnnotateNodeSignal::AnnotationLevel::Info)
{
}
NodeAnnotationTreeItem::NodeAnnotationTreeItem(ScriptCanvas::AnnotateNodeSignal::AnnotationLevel annotationLevel, const AZStd::string& annotation)
: m_annotationLevel(annotationLevel)
, m_annotation(annotation.c_str())
{
switch (m_annotationLevel)
{
case ScriptCanvas::AnnotateNodeSignal::AnnotationLevel::Info:
m_annotationIcon = QIcon(":/ScriptCanvasEditorResources/Resources/message_icon.png");
break;
case ScriptCanvas::AnnotateNodeSignal::AnnotationLevel::Warning:
m_annotationIcon = QIcon(":/ScriptCanvasEditorResources/Resources/warning_symbol.png");
break;
case ScriptCanvas::AnnotateNodeSignal::AnnotationLevel::Error:
m_annotationIcon = QIcon(":/ScriptCanvasEditorResources/Resources/error_icon.png");
break;
default:
break;
}
}
QVariant NodeAnnotationTreeItem::Data(const QModelIndex& index, int role) const
{
// We are spanned, we we only have a single column
if (index.column() == DebugLogTreeItem::Column::NodeName)
{
switch (role)
{
case Qt::DecorationRole:
{
return m_annotationIcon;
}
case Qt::DisplayRole:
{
return m_annotation;
}
case Qt::ToolTipRole:
{
return m_annotation;
}
default:
break;
}
}
return QVariant();
}
bool NodeAnnotationTreeItem::OnMatchesFilter(const DebugLogFilter& treeFilter)
{
bool matches = false;
matches = matches || (m_annotation.lastIndexOf(treeFilter.m_filter) >= 0);
return matches;
}
}
@@ -0,0 +1,288 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/PlatformDef.h>
// qdatetime.h(331): warning C4251: 'QDateTime::d': class 'QSharedDataPointer<QDateTimePrivate>' needs to have dll-interface to be used by clients of class 'QDateTime'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
#include <QIcon>
#include <QTime>
#include <QTimer>
AZ_POP_DISABLE_WARNING
#include <AzCore/Component/NamedEntityId.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/chrono/chrono.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Components/StyleBus.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <ScriptCanvas/Core/Endpoint.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingTypes.h>
#include <Editor/Include/ScriptCanvas/Bus/EditorScriptCanvasBus.h>
namespace ScriptCanvasEditor
{
class DebugLogFilter
{
public:
QRegExp m_filter;
bool IsEmpty() const
{
return m_filter.isEmpty();
}
};
class DebugLogTreeItem
: public GraphCanvas::GraphCanvasTreeItem
{
public:
AZ_CLASS_ALLOCATOR(DebugLogTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(DebugLogTreeItem, "{E0B2A52B-47A4-40FF-A76F-4655125D01CC}", GraphCanvas::GraphCanvasTreeItem);
enum Column
{
IndexForce = -1,
NodeName,
Input,
Output,
TimeStep,
ScriptName,
SourceEntity,
Count
};
bool MatchesFilter(const DebugLogFilter& treeFilter);
const ScriptCanvas::Endpoint& GetIncitingEndpoint() const;
bool IsTriggeredBy(const ScriptCanvas::Endpoint& endpoint) const;
Qt::ItemFlags Flags(const QModelIndex& index) const override final;
int GetColumnCount() const override final;
protected:
void SetIncitingEndpoint(const ScriptCanvas::Endpoint& endpoint);
virtual bool OnMatchesFilter(const DebugLogFilter& treeFilter) = 0;
private:
ScriptCanvas::Endpoint m_incitingEndpoint;
};
class ExecutionLogTreeItem;
class DebugLogRootItem
: public DebugLogTreeItem
{
public:
enum UpdatePolicy
{
RealTime,
Batched,
SingleTime
};
AZ_CLASS_ALLOCATOR(DebugLogRootItem, AZ::SystemAllocator, 0);
AZ_RTTI(DebugLogRootItem, "{CF59F72E-04AC-415C-A2F2-99D79564730B}", DebugLogTreeItem);
DebugLogRootItem();
~DebugLogRootItem();
ExecutionLogTreeItem* CreateExecutionItem(const LoggingDataId& loggingDataId, const ScriptCanvas::NodeTypeIdentifier& nodeType, const ScriptCanvas::GraphInfo& graphInfo, const ScriptCanvas::NamedNodeId& nodeId);
QVariant Data(const QModelIndex& index, int role) const override final;
void ResetData();
void SetUpdatePolicy(UpdatePolicy updatePolicy);
UpdatePolicy GetUpdatePolicy() const;
void RedoLayout();
protected:
bool OnMatchesFilter([[maybe_unused]] const DebugLogFilter& treeFilter) { return true; }
UpdatePolicy m_updatePolicy;
QTimer m_additionTimer;
};
class ExecutionLogTreeItem
: public DebugLogTreeItem
, public GraphCanvas::StyleManagerNotificationBus::Handler
, public EditorGraphNotificationBus::Handler
, public GeneralAssetNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ExecutionLogTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(ExecutionLogTreeItem, "{71139142-A30C-4A16-81CC-D51314AEAF7D}", DebugLogTreeItem);
ExecutionLogTreeItem(const LoggingDataId& loggingDataId, const ScriptCanvas::NodeTypeIdentifier& nodeType, const ScriptCanvas::GraphInfo& graphInfo, const ScriptCanvas::NamedNodeId& nodeId);
~ExecutionLogTreeItem() override = default;
QVariant Data(const QModelIndex& index, int role) const override final;
AZ::EntityId GetNodeId() const;
void RegisterAnnotation(const ScriptCanvas::AnnotateNodeSignal& annotationSignal, bool allowAddSignal);
void RegisterDataInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::string_view dataString, bool allowAddSignal);
void RegisterDataOutput(const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::string_view dataString, bool allowAddSignal);
void RegisterExecutionInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::chrono::milliseconds relativeExecution);
bool HasExecutionInput() const;
void RegisterExecutionOutput(const ScriptCanvas::SlotId& slotId, AZStd::string_view slotName, AZStd::chrono::milliseconds relativeExecution);
bool HasExecutionOutput() const;
// GraphCanvas::StyleManagerNotificationBus
void OnStylesUnloaded() override;
void OnStylesLoaded() override;
////
// GeneralNotificationsBus
void OnAssetVisualized() override;
void OnAssetUnloaded() override;
////
// EditorGraphNotificationBus
void OnGraphCanvasSceneDisplayed() override;
////
const ScriptCanvas::GraphIdentifier& GetGraphIdentifier() const;
const AZ::Data::AssetId& GetAssetId() const;
AZ::EntityId GetScriptCanvasAssetNodeId() const;
GraphCanvas::NodeId GetGraphCanvasNodeId() const;
protected:
bool OnMatchesFilter(const DebugLogFilter& treeFilter) override;
private:
void ResolveWrapperNode(bool refreshData = true);
void ScrapeBehaviorContextData();
void ScrapeGraphCanvasData();
void PopulateInputSlotData();
void PopulateOutputSlotData();
LoggingDataId m_loggingDataId;
ScriptCanvas::NodeTypeIdentifier m_nodeType;
ScriptCanvas::GraphInfo m_graphInfo;
QString m_sourceEntityName;
QString m_graphName;
QString m_relativeGraphPath;
AZ::EntityId m_graphCanvasGraphId;
AZ::EntityId m_scriptCanvasAssetNodeId;
AZ::EntityId m_scriptCanvasNodeId;
GraphCanvas::NodeId m_graphCanvasNodeId;
QString m_displayName;
ScriptCanvas::SlotId m_inputSlot;
QString m_inputName;
ScriptCanvas::SlotId m_outputSlot;
QString m_outputName;
QString m_timeString;
GraphCanvas::PaletteIconConfiguration m_paletteConfiguration;
const QPixmap* m_iconPixmap;
};
class DataLogTreeItem
: public DebugLogTreeItem
{
friend class ExecutionLogTreeItem;
public:
AZ_CLASS_ALLOCATOR(DataLogTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(DataLogTreeItem, "{04D997AD-E3CA-47CA-9810-8814B36AB726}", DebugLogTreeItem);
DataLogTreeItem(const ScriptCanvas::GraphIdentifier& graphIdentifier);
QVariant Data(const QModelIndex& index, int role) const override final;
void RegisterDataInput(const ScriptCanvas::Endpoint& incitingEndpoint, const ScriptCanvas::Endpoint& endpoint, AZStd::string_view slotName, AZStd::string_view data);
bool HasInput() const;
void RegisterDataOutput(const ScriptCanvas::Endpoint& endpoint, AZStd::string_view slotName, AZStd::string_view dataString);
bool HasOutput() const;
protected:
bool OnMatchesFilter(const DebugLogFilter& treeFilter) override;
bool LessThan(const GraphCanvas::GraphCanvasTreeItem* graphItem) const override;
private:
AZ::Data::AssetId GetAssetId() const;
void ScrapeData();
void InvalidateEditorIds();
void InvalidateGraphCanvasIds();
void ScrapeInputName();
void ScrapeOutputName();
ScriptCanvas::GraphIdentifier m_graphIdentifier;
GraphCanvas::GraphId m_graphCanvasGraphId;
ScriptCanvas::Endpoint m_assetInputEndpoint;
QString m_inputName;
QString m_inputData;
ScriptCanvas::Endpoint m_assetOutputEndpoint;
QString m_outputName;
QString m_outputData;
};
class NodeAnnotationTreeItem
: public DebugLogTreeItem
{
public:
AZ_CLASS_ALLOCATOR(NodeAnnotationTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(NodeAnnotationTreeItem, "{4A052945-F8D1-4A96-8D52-D8C20504E30F}", DebugLogTreeItem);
NodeAnnotationTreeItem();
NodeAnnotationTreeItem(ScriptCanvas::AnnotateNodeSignal::AnnotationLevel annotationLevel, const AZStd::string& annotation);
~NodeAnnotationTreeItem() override = default;
QVariant Data(const QModelIndex& index, int role) const override final;
protected:
bool OnMatchesFilter(const DebugLogFilter& treeFilter) override;
private:
ScriptCanvas::AnnotateNodeSignal::AnnotationLevel m_annotationLevel;
QString m_annotation;
QIcon m_annotationIcon;
};
}
@@ -0,0 +1,345 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <Editor/View/Widgets/LoggingPanel/PivotTree/EntityPivotTree/EntityPivotTree.h>
namespace ScriptCanvasEditor
{
/////////////////////////////
// EntityPivotTreeGraphItem
/////////////////////////////
EntityPivotTreeGraphItem::EntityPivotTreeGraphItem(const ScriptCanvas::GraphIdentifier& graphIdentifier)
: PivotTreeGraphItem(graphIdentifier.m_assetId)
, m_checkState(Qt::CheckState::Unchecked)
, m_graphIdentifier(graphIdentifier)
{
}
Qt::CheckState EntityPivotTreeGraphItem::GetCheckState() const
{
return m_checkState;
}
void EntityPivotTreeGraphItem::SetCheckState(Qt::CheckState checkState)
{
m_checkState = checkState;
SignalDataChanged();
}
const ScriptCanvas::GraphIdentifier& EntityPivotTreeGraphItem::GetGraphIdentifier() const
{
return m_graphIdentifier;
}
//////////////////////////////
// EntityPivotTreeEntityItem
//////////////////////////////
EntityPivotTreeEntityItem::EntityPivotTreeEntityItem(const AZ::NamedEntityId& entityId)
: PivotTreeEntityItem(entityId)
, m_checkState(Qt::CheckState::Unchecked)
{
const bool isPivotElement = true;
SetIsPivotedElement(isPivotElement);
}
void EntityPivotTreeEntityItem::RegisterGraphIdentifier(const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_pivotItems.find(graphIdentifier);
if (mapIter == m_pivotItems.end())
{
EntityPivotTreeGraphItem* graphItem = CreateChildNode<EntityPivotTreeGraphItem>(graphIdentifier);
m_pivotItems[graphIdentifier] = graphItem;
if (m_checkState != Qt::CheckState::PartiallyChecked)
{
graphItem->SetCheckState(m_checkState);
}
}
}
void EntityPivotTreeEntityItem::OnChildDataChanged(GraphCanvasTreeItem* treeItem)
{
EntityPivotTreeGraphItem* graphItem = static_cast<EntityPivotTreeGraphItem*>(treeItem);
ScriptCanvas::GraphIdentifier graphIdentifier = graphItem->GetGraphIdentifier();
if (graphItem->GetCheckState() == Qt::CheckState::Checked)
{
LoggingDataRequestBus::Event(GetLoggingDataId(), &LoggingDataRequests::EnableRegistration, GetNamedEntityId(), graphIdentifier);
}
else
{
LoggingDataRequestBus::Event(GetLoggingDataId(), &LoggingDataRequests::DisableRegistration, GetNamedEntityId(), graphIdentifier);
}
CalculateCheckState();
SignalDataChanged();
}
void EntityPivotTreeEntityItem::UnregisterGraphIdentifier(const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_pivotItems.find(graphIdentifier);
if (mapIter != m_pivotItems.end())
{
RemoveChild(mapIter->second);
m_pivotItems.erase(mapIter);
}
}
Qt::CheckState EntityPivotTreeEntityItem::GetCheckState() const
{
return m_checkState;
}
void EntityPivotTreeEntityItem::SetCheckState(Qt::CheckState checkState)
{
if (m_checkState != checkState)
{
m_checkState = checkState;
for (const auto& mapIter : m_pivotItems)
{
mapIter.second->SetCheckState(checkState);
}
SignalDataChanged();
}
}
EntityPivotTreeGraphItem* EntityPivotTreeEntityItem::FindGraphTreeItem(const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_pivotItems.find(graphIdentifier);
if (mapIter != m_pivotItems.end())
{
return mapIter->second;
}
else
{
return nullptr;
}
}
void EntityPivotTreeEntityItem::OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (namedEntityId == GetEntityId())
{
EntityPivotTreeGraphItem* item = FindGraphTreeItem(graphIdentifier);
if (item)
{
if (isEnabled)
{
item->SetCheckState(Qt::CheckState::Checked);
}
else
{
item->SetCheckState(Qt::CheckState::Unchecked);
}
}
}
}
void EntityPivotTreeEntityItem::OnLoggingDataIdSet()
{
LoggingDataNotificationBus::Handler::BusDisconnect();
LoggingDataNotificationBus::Handler::BusConnect(GetLoggingDataId());
}
void EntityPivotTreeEntityItem::CalculateCheckState()
{
bool isChecked = false;
bool isUnchecked = false;
for (const auto& mapIter : m_pivotItems)
{
if (mapIter.second->GetCheckState() == Qt::CheckState::Checked)
{
isChecked = true;
}
else
{
isUnchecked = true;
}
if (isChecked && isUnchecked)
{
break;
}
}
m_checkState = Qt::CheckState::Unchecked;
if (isChecked && isUnchecked)
{
m_checkState = Qt::CheckState::PartiallyChecked;
}
else if (isChecked)
{
m_checkState = Qt::CheckState::Checked;
}
}
////////////////////////
// EntityPivotTreeRoot
////////////////////////
EntityPivotTreeRoot::EntityPivotTreeRoot()
: m_capturingData(false)
{
}
void EntityPivotTreeRoot::OnDataSourceChanged(const LoggingDataId& aggregateDataSource)
{
ClearData();
LoggingDataNotificationBus::Handler::BusDisconnect();
m_dataSource = aggregateDataSource;
const LoggingDataAggregator* dataAggregator = nullptr;
LoggingDataRequestBus::EventResult(dataAggregator, m_dataSource, &LoggingDataRequests::FindLoggingData);
if (dataAggregator)
{
const EntityGraphRegistrationMap& registrationMap = dataAggregator->GetEntityGraphRegistrationMap();
for (const auto& mapIter : registrationMap)
{
OnEntityGraphRegistered(mapIter.first, mapIter.second);
}
if (dataAggregator->IsCapturingData())
{
OnDataCaptureBegin();
}
}
LoggingDataNotificationBus::Handler::BusConnect(m_dataSource);
}
void EntityPivotTreeRoot::OnDataCaptureBegin()
{
m_capturingData = true;
}
void EntityPivotTreeRoot::OnDataCaptureEnd()
{
m_capturingData = false;
for (const auto& unregistrationPair : m_delayedUnregistrations)
{
OnEntityGraphUnregistered(unregistrationPair.first, unregistrationPair.second);
}
m_delayedUnregistrations.clear();
}
void EntityPivotTreeRoot::OnEntityGraphRegistered(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
EntityPivotTreeEntityItem* pivotItem = nullptr;
auto mapIter = m_entityTreeItemMapping.find(namedEntityId);
if (mapIter != m_entityTreeItemMapping.end())
{
pivotItem = mapIter->second;
}
else
{
pivotItem = CreateChildNode<EntityPivotTreeEntityItem>(namedEntityId);
m_entityTreeItemMapping[namedEntityId] = pivotItem;
}
if (pivotItem)
{
pivotItem->RegisterGraphIdentifier(graphIdentifier);
}
}
void EntityPivotTreeRoot::OnEntityGraphUnregistered(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_entityTreeItemMapping.find(namedEntityId);
if (mapIter != m_entityTreeItemMapping.end())
{
EntityPivotTreeEntityItem* pivotItem = mapIter->second;
if (!m_capturingData)
{
pivotItem->UnregisterGraphIdentifier(graphIdentifier);
if (pivotItem->GetChildCount() == 0)
{
RemoveChild(pivotItem);
m_entityTreeItemMapping.erase(mapIter);
}
}
else
{
m_delayedUnregistrations.emplace_back(namedEntityId, graphIdentifier);
}
}
}
void EntityPivotTreeRoot::OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
return;
}
auto mapIter = m_entityTreeItemMapping.find(namedEntityId);
if (mapIter != m_entityTreeItemMapping.end())
{
EntityPivotTreeGraphItem* pivotTreeItem = mapIter->second->FindGraphTreeItem(graphIdentifier);
if (pivotTreeItem)
{
if (isEnabled)
{
pivotTreeItem->SetCheckState(Qt::CheckState::Checked);
}
else
{
pivotTreeItem->SetCheckState(Qt::CheckState::Unchecked);
}
}
}
}
void EntityPivotTreeRoot::ClearData()
{
ClearChildren();
m_entityTreeItemMapping.clear();
}
//////////////////////////
// EntityPivotTreeWidget
//////////////////////////
EntityPivotTreeWidget::EntityPivotTreeWidget(QWidget* parent)
: PivotTreeWidget(aznew EntityPivotTreeRoot(), AZ_CRC("EntityPivotTreeId", 0xd44255d6), parent)
{
}
#include <Editor/View/Widgets/LoggingPanel/PivotTree/EntityPivotTree/moc_EntityPivotTree.cpp>
}
@@ -0,0 +1,124 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/std/containers/unordered_map.h>
#include <Editor/View/Widgets/LoggingPanel/PivotTree/PivotTreeWidget.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#endif
namespace ScriptCanvasEditor
{
class EntityPivotTreeGraphItem
: public PivotTreeGraphItem
{
friend class EntityPivotTreeEntityItem;
public:
AZ_CLASS_ALLOCATOR(EntityPivotTreeGraphItem, AZ::SystemAllocator, 0);
AZ_RTTI(EntityPivotTreeGraphItem, "{CE064D69-D478-4594-A596-5DBE0DE46F6E}", PivotTreeGraphItem);
EntityPivotTreeGraphItem(const ScriptCanvas::GraphIdentifier& graphIdentifier);
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState checkState) override final;
const ScriptCanvas::GraphIdentifier& GetGraphIdentifier() const;
private:
Qt::CheckState m_checkState;
ScriptCanvas::GraphIdentifier m_graphIdentifier;
};
class EntityPivotTreeEntityItem
: public PivotTreeEntityItem
, public LoggingDataNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(EntityPivotTreeEntityItem, AZ::SystemAllocator, 0);
AZ_RTTI(EntityPivotTreeEntityItem, "{027A8617-4095-46F1-B9AD-49E360C90C73}", PivotTreeEntityItem);
EntityPivotTreeEntityItem(const AZ::NamedEntityId& entityId);
void RegisterGraphIdentifier(const ScriptCanvas::GraphIdentifier& graphIdentifier);
void UnregisterGraphIdentifier(const ScriptCanvas::GraphIdentifier& graphIdentifier);
void OnChildDataChanged(GraphCanvasTreeItem* treeItem) override;
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState debugging) override final;
EntityPivotTreeGraphItem* FindGraphTreeItem(const ScriptCanvas::GraphIdentifier& registrationData);
// LoggingDataNotificationBus
void OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& registrationData) override;
////
protected:
void OnLoggingDataIdSet() override;
private:
void CalculateCheckState();
Qt::CheckState m_checkState;
AZStd::unordered_map< ScriptCanvas::GraphIdentifier, EntityPivotTreeGraphItem*> m_pivotItems;
};
class EntityPivotTreeRoot
: public PivotTreeRoot
, public LoggingDataNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(EntityPivotTreeRoot, AZ::SystemAllocator, 0);
AZ_RTTI(EntityPivotTreeRoot, "{93DE206E-CE31-4A59-BEBF-87B26E5A28D2}", PivotTreeRoot);
EntityPivotTreeRoot();
// PivotTreeRoot
void OnDataSourceChanged(const LoggingDataId& aggregateDataSource) override;
////
// LoggedDataNotifications
void OnDataCaptureBegin() override;
void OnDataCaptureEnd() override;
void OnEntityGraphRegistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& assetId) override;
void OnEntityGraphUnregistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& assetId) override;
void OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& registrationData) override;
////
private:
void ClearData();
LoggingDataId m_dataSource;
AZStd::unordered_map< AZ::EntityId, EntityPivotTreeEntityItem* > m_entityTreeItemMapping;
AZStd::vector < AZStd::pair < AZ::NamedEntityId, ScriptCanvas::GraphIdentifier > > m_delayedUnregistrations;
bool m_capturingData;
};
class EntityPivotTreeWidget
: public PivotTreeWidget
{
Q_OBJECT
public:
EntityPivotTreeWidget(QWidget* parent);
};
}
@@ -0,0 +1,583 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
#include <Editor/View/Widgets/LoggingPanel/PivotTree/GraphPivotTree/GraphPivotTree.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
namespace ScriptCanvasEditor
{
/////////////////////////////
// GraphPivotTreeEntityItem
/////////////////////////////
GraphPivotTreeEntityItem::GraphPivotTreeEntityItem(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
: PivotTreeEntityItem(namedEntityId)
, m_checkState(Qt::CheckState::Unchecked)
, m_graphIdentifier(graphIdentifier)
{
}
Qt::CheckState GraphPivotTreeEntityItem::GetCheckState() const
{
return m_checkState;
}
void GraphPivotTreeEntityItem::SetCheckState(Qt::CheckState checkState)
{
m_checkState = checkState;
SignalDataChanged();
}
const ScriptCanvas::GraphIdentifier& GraphPivotTreeEntityItem::GetGraphIdentifier() const
{
return m_graphIdentifier;
}
////////////////////////////
// GraphPivotTreeGraphItem
////////////////////////////
GraphPivotTreeGraphItem::GraphPivotTreeGraphItem(const AZ::Data::AssetId& assetId)
: PivotTreeGraphItem(assetId)
, m_checkState(Qt::CheckState::Unchecked)
{
const bool isPivotedElement = true;
SetIsPivotedElement(isPivotedElement);
const bool isChecked = true;
SetupDynamicallySpawnedElementItem(isChecked);
}
void GraphPivotTreeGraphItem::OnDataSwitch()
{
bool isChecked = true;
auto pivotIter = m_pivotItems.find(AZ::EntityId());
if (pivotIter != m_pivotItems.end())
{
GraphPivotTreeEntityItem* entityItem = pivotIter->second;
isChecked = entityItem->GetCheckState() == Qt::CheckState::Checked;
}
m_pivotItems.clear();
ClearChildren();
SetupDynamicallySpawnedElementItem(isChecked);
}
void GraphPivotTreeGraphItem::RegisterEntity(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto pivotRange = m_pivotItems.equal_range(entityId);
bool foundElement = false;
for (auto mapIter = pivotRange.first; mapIter != pivotRange.second; ++mapIter)
{
if (mapIter->second->GetGraphIdentifier() == graphIdentifier)
{
foundElement = true;
break;
}
}
if (!foundElement)
{
GraphPivotTreeEntityItem* entityItem = CreateChildNode<GraphPivotTreeEntityItem>(entityId, graphIdentifier);
entityItem->SetCheckState(Qt::CheckState::Unchecked);
m_pivotItems.insert(AZStd::make_pair(entityId,entityItem));
}
}
void GraphPivotTreeGraphItem::UnregisterEntity(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto pivotRange = m_pivotItems.equal_range(entityId);
for (auto pivotIter = pivotRange.first; pivotIter != pivotRange.second; ++pivotIter)
{
if (pivotIter->second->GetGraphIdentifier() == graphIdentifier)
{
RemoveChild(pivotIter->second);
m_pivotItems.erase(pivotIter);
break;
}
}
}
void GraphPivotTreeGraphItem::OnChildDataChanged(GraphCanvasTreeItem* treeItem)
{
GraphPivotTreeEntityItem* graphItem = static_cast<GraphPivotTreeEntityItem*>(treeItem);
if (graphItem->GetCheckState() == Qt::CheckState::Checked)
{
LoggingDataRequestBus::Event(GetLoggingDataId(), &LoggingDataRequests::EnableRegistration, graphItem->GetNamedEntityId(), graphItem->GetGraphIdentifier());
}
else
{
LoggingDataRequestBus::Event(GetLoggingDataId(), &LoggingDataRequests::DisableRegistration, graphItem->GetNamedEntityId(), graphItem->GetGraphIdentifier());
}
CalculateCheckState();
SignalDataChanged();
}
Qt::CheckState GraphPivotTreeGraphItem::GetCheckState() const
{
return m_checkState;
}
void GraphPivotTreeGraphItem::SetCheckState(Qt::CheckState checkState)
{
if (checkState != m_checkState)
{
m_checkState = checkState;
for (int i = 0; i < GetChildCount(); ++i)
{
PivotTreeItem* treeItem = static_cast<PivotTreeItem*>(FindChildByRow(i));
if (treeItem)
{
treeItem->SetCheckState(checkState);
}
}
SignalDataChanged();
}
}
GraphPivotTreeEntityItem* GraphPivotTreeGraphItem::FindDynamicallySpawnedTreeItem() const
{
return FindEntityTreeItem(AZ::NamedEntityId(AZ::EntityId(), ""), ScriptCanvas::GraphIdentifier(GetAssetId(), k_dynamicallySpawnedControllerId));
}
GraphPivotTreeEntityItem* GraphPivotTreeGraphItem::FindEntityTreeItem(const AZ::NamedEntityId& namedEntityId, [[maybe_unused]] const ScriptCanvas::GraphIdentifier& graphIdentifier) const
{
auto pivotIter = m_pivotItems.find(namedEntityId);
if (pivotIter != m_pivotItems.end())
{
return pivotIter->second;
}
return nullptr;
}
void GraphPivotTreeGraphItem::OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
if (graphIdentifier.m_assetId == GetAssetId())
{
GraphPivotTreeEntityItem* item = FindEntityTreeItem(namedEntityId, graphIdentifier);
if (item)
{
if (isEnabled)
{
item->SetCheckState(Qt::CheckState::Checked);
}
else
{
item->SetCheckState(Qt::CheckState::Unchecked);
}
}
}
}
void GraphPivotTreeGraphItem::OnLoggingDataIdSet()
{
LoggingDataNotificationBus::Handler::BusDisconnect();
LoggingDataNotificationBus::Handler::BusConnect(GetLoggingDataId());
}
void GraphPivotTreeGraphItem::SetupDynamicallySpawnedElementItem([[maybe_unused]] bool isChecked)
{
AZ::NamedEntityId dynamicEntityId(AZ::EntityId(), "All Graph Instances");
RegisterEntity(dynamicEntityId, ScriptCanvas::GraphIdentifier(GetAssetId(), k_dynamicallySpawnedControllerId));
}
void GraphPivotTreeGraphItem::CalculateCheckState()
{
bool isChecked = false;
bool isUnchecked = false;
for (int i = 0; i < GetChildCount(); ++i)
{
PivotTreeItem* treeItem = static_cast<PivotTreeItem*>(FindChildByRow(i));
if (treeItem)
{
if (treeItem->GetCheckState() == Qt::CheckState::Checked)
{
isChecked = true;
}
else
{
isUnchecked = true;
}
if (isChecked && isUnchecked)
{
break;
}
}
}
if (isChecked && isUnchecked)
{
m_checkState = Qt::CheckState::PartiallyChecked;
}
else if (isChecked)
{
m_checkState = Qt::CheckState::Checked;
}
else
{
m_checkState = Qt::CheckState::Unchecked;
}
}
/////////////////////////
// GraphPivotTreeFolder
/////////////////////////
GraphPivotTreeFolder::GraphPivotTreeFolder(AZStd::string_view folder)
: m_folderName(folder)
, m_checkState(Qt::CheckState::Unchecked)
{
const bool isPivotElement = true;
SetIsPivotedElement(isPivotElement);
}
Qt::CheckState GraphPivotTreeFolder::GetCheckState() const
{
return m_checkState;
}
void GraphPivotTreeFolder::SetCheckState(Qt::CheckState checkState)
{
if (m_checkState != checkState)
{
m_checkState = checkState;
for (int i = 0; i < GetChildCount(); ++i)
{
PivotTreeItem* pivotTreeItem = static_cast<PivotTreeItem*>(FindChildByRow(i));
pivotTreeItem->SetCheckState(checkState);
}
SignalDataChanged();
}
}
AZStd::string GraphPivotTreeFolder::GetDisplayName() const
{
return m_folderName;
}
void GraphPivotTreeFolder::OnChildDataChanged([[maybe_unused]] GraphCanvasTreeItem* treeItem)
{
CalculateCheckState();
SignalDataChanged();
}
void GraphPivotTreeFolder::CalculateCheckState()
{
bool isChecked = false;
bool isUnchecked = false;
for (int i = 0; i < GetChildCount(); ++i)
{
const PivotTreeItem* pivotTreeItem = static_cast<const PivotTreeItem*>(FindChildByRow(i));
if (pivotTreeItem)
{
Qt::CheckState checkState = pivotTreeItem->GetCheckState();
if (checkState == Qt::CheckState::PartiallyChecked)
{
isChecked = true;
isUnchecked = true;
}
else if (checkState == Qt::CheckState::Checked)
{
isChecked = true;
}
else
{
isUnchecked = true;
}
if (isChecked && isUnchecked)
{
break;
}
}
}
if (isChecked && isUnchecked)
{
m_checkState = Qt::CheckState::PartiallyChecked;
}
else if (isChecked)
{
m_checkState = Qt::CheckState::Checked;
}
else
{
m_checkState = Qt::CheckState::Unchecked;
}
SignalDataChanged();
}
///////////////////////
// GraphPivotTreeRoot
///////////////////////
GraphPivotTreeRoot::GraphPivotTreeRoot()
: m_categorizer((*this))
{
AzToolsFramework::AssetBrowser::AssetBrowserModel* assetBrowserModel = nullptr;
AzToolsFramework::AssetBrowser::AssetBrowserComponentRequestBus::BroadcastResult(assetBrowserModel, &AzToolsFramework::AssetBrowser::AssetBrowserComponentRequests::GetAssetBrowserModel);
m_assetModel = new AzToolsFramework::AssetBrowser::AssetBrowserFilterModel();
AzToolsFramework::AssetBrowser::AssetGroupFilter* assetFilter = new AzToolsFramework::AssetBrowser::AssetGroupFilter();
assetFilter->SetAssetGroup(ScriptCanvasEditor::ScriptCanvasAsset::Description::GetGroup(azrtti_typeid<ScriptCanvasAsset>()));
assetFilter->SetFilterPropagation(AzToolsFramework::AssetBrowser::AssetBrowserEntryFilter::PropagateDirection::Down);
QObject::connect(m_assetModel, &QAbstractItemModel::rowsInserted, this, &GraphPivotTreeRoot::OnScriptCanvasGraphAssetAdded);
QObject::connect(m_assetModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &GraphPivotTreeRoot::OnScriptCanvasGraphAssetRemoved);
m_assetModel->setSourceModel(assetBrowserModel);
SetAllowPruneOnEmpty(false);
}
void GraphPivotTreeRoot::OnDataSourceChanged(const LoggingDataId& aggregateDataSource)
{
if (LoggingDataNotificationBus::Handler::BusIsConnected())
{
LoggingDataNotificationBus::Handler::BusDisconnect();
}
m_loggedDataId = aggregateDataSource;
for (auto assetPair : m_graphTreeItemMapping)
{
assetPair.second->OnDataSwitch();
}
const LoggingDataAggregator* dataAggregator = nullptr;
LoggingDataRequestBus::EventResult(dataAggregator, m_loggedDataId, &LoggingDataRequests::FindLoggingData);
if (dataAggregator)
{
const EntityGraphRegistrationMap& entityPivoting = dataAggregator->GetEntityGraphRegistrationMap();
for (const auto& registrationMap : entityPivoting)
{
OnEntityGraphUnregistered(registrationMap.first, registrationMap.second);
}
if (dataAggregator->IsCapturingData())
{
OnDataCaptureBegin();
}
}
LoggingDataNotificationBus::Handler::BusConnect(m_loggedDataId);
}
void GraphPivotTreeRoot::OnDataCaptureBegin()
{
}
void GraphPivotTreeRoot::OnDataCaptureEnd()
{
}
void GraphPivotTreeRoot::OnEntityGraphRegistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
GraphPivotTreeGraphItem* graphItem = nullptr;
auto mapPairIter = m_graphTreeItemMapping.find(graphIdentifier.m_assetId);
if (mapPairIter == m_graphTreeItemMapping.end())
{
AZStd::string fullPath;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(fullPath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, graphIdentifier.m_assetId);
GraphCanvasTreeItem* parentItem = m_categorizer.GetCategoryNode(fullPath.c_str(), this);
graphItem = parentItem->CreateChildNode<GraphPivotTreeGraphItem>(graphIdentifier.m_assetId);
m_graphTreeItemMapping[graphIdentifier.m_assetId] = graphItem;
}
else
{
graphItem = mapPairIter->second;
}
if (entityId.IsValid())
{
graphItem->RegisterEntity(entityId, graphIdentifier);
}
}
void GraphPivotTreeRoot::OnEntityGraphUnregistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapPairIter = m_graphTreeItemMapping.find(graphIdentifier.m_assetId);
if (mapPairIter != m_graphTreeItemMapping.end())
{
mapPairIter->second->UnregisterEntity(entityId, graphIdentifier);
if (mapPairIter->second->GetChildCount() == 0)
{
m_categorizer.PruneEmptyNodes();
}
}
}
void GraphPivotTreeRoot::OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier)
{
auto mapIter = m_graphTreeItemMapping.find(graphIdentifier.m_assetId);
if (mapIter != m_graphTreeItemMapping.end())
{
GraphPivotTreeEntityItem* pivotTreeItem = nullptr;
if (graphIdentifier.m_componentId == k_dynamicallySpawnedControllerId)
{
pivotTreeItem = mapIter->second->FindDynamicallySpawnedTreeItem();
}
else
{
pivotTreeItem = mapIter->second->FindEntityTreeItem(namedEntityId, graphIdentifier);
}
if (pivotTreeItem)
{
if (isEnabled)
{
pivotTreeItem->SetCheckState(Qt::CheckState::Checked);
}
else
{
pivotTreeItem->SetCheckState(Qt::CheckState::Unchecked);
}
}
}
}
GraphCanvas::GraphCanvasTreeItem* GraphPivotTreeRoot::CreateCategoryNode([[maybe_unused]] AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvasTreeItem* parent) const
{
return parent->CreateChildNode<GraphPivotTreeFolder>(categoryName);
}
void GraphPivotTreeRoot::OnScriptCanvasGraphAssetAdded(const QModelIndex& parentIndex, int first, int last)
{
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = m_assetModel->index(i, 0, parentIndex);
QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessEntry(entry);
}
}
void GraphPivotTreeRoot::OnScriptCanvasGraphAssetRemoved(const QModelIndex& parentIndex, int first, int last)
{
// TODO: This likely needs to be handled better
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = m_assetModel->index(i, 0, parentIndex);
QModelIndex sourceIndex = m_assetModel->mapToSource(modelIndex);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
if (entry && entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = azrtti_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
if (productEntry->GetAssetType() == azrtti_typeid<ScriptCanvasEditor::ScriptCanvasAsset>())
{
auto mapIter = m_graphTreeItemMapping.find(productEntry->GetAssetId());
if (mapIter != m_graphTreeItemMapping.end() && mapIter->second)
{
GraphCanvas::GraphCanvasTreeItem* currentItem = mapIter->second;
currentItem->ClearChildren();
m_graphTreeItemMapping.erase(mapIter);
m_categorizer.PruneNode(currentItem);
}
OnEntityGraphUnregistered(AZ::NamedEntityId(), ScriptCanvas::GraphIdentifier(productEntry->GetAssetId(), k_dynamicallySpawnedControllerId));
}
}
}
}
void GraphPivotTreeRoot::ProcessEntry(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
if (entry && entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = static_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
if (productEntry->GetAssetType() == azrtti_typeid<ScriptCanvasEditor::ScriptCanvasAsset>())
{
OnEntityGraphRegistered(AZ::NamedEntityId(), ScriptCanvas::GraphIdentifier(productEntry->GetAssetId(), k_dynamicallySpawnedControllerId));
}
}
}
void GraphPivotTreeRoot::TraverseTree(QModelIndex index)
{
QModelIndex sourceIndex = m_assetModel->mapToSource(index);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessEntry(entry);
int rowCount = m_assetModel->rowCount(index);
for (int i = 0; i < rowCount; ++i)
{
QModelIndex nextIndex = m_assetModel->index(i, 0, index);
TraverseTree(nextIndex);
}
}
/////////////////////////
// GraphPivotTreeWidget
/////////////////////////
GraphPivotTreeWidget::GraphPivotTreeWidget(QWidget* parent)
: PivotTreeWidget(aznew GraphPivotTreeRoot(), AZ_CRC("GraphPivotTreeId", 0xed815ba3), parent)
{
static_cast<GraphPivotTreeRoot*>(GetTreeRoot())->TraverseTree();
}
#include <Editor/View/Widgets/LoggingPanel/PivotTree/GraphPivotTree/moc_GraphPivotTree.cpp>
}
@@ -0,0 +1,179 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeCategorizer.h>
#include <Editor/View/Widgets/LoggingPanel/PivotTree/PivotTreeWidget.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingDataAggregator.h>
#endif
namespace ScriptCanvasEditor
{
class GraphPivotTreeEntityItem
: public PivotTreeEntityItem
{
friend class EntityPivotTreeEntityItem;
public:
AZ_CLASS_ALLOCATOR(GraphPivotTreeEntityItem, AZ::SystemAllocator, 0);
AZ_RTTI(GraphPivotTreeEntityItem, "{17B2C45B-D63B-458E-9A2F-ED0A8218A77B}", PivotTreeEntityItem);
GraphPivotTreeEntityItem(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState checkState) override final;
const ScriptCanvas::GraphIdentifier& GetGraphIdentifier() const;
private:
Qt::CheckState m_checkState;
ScriptCanvas::GraphIdentifier m_graphIdentifier;
};
class GraphPivotTreeGraphItem
: public PivotTreeGraphItem
, public LoggingDataNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(GraphPivotTreeGraphItem, AZ::SystemAllocator, 0);
AZ_RTTI(GraphPivotTreeGraphItem, "{9B449D02-109E-4D9E-BA99-35C52106432C}", PivotTreeGraphItem);
GraphPivotTreeGraphItem(const AZ::Data::AssetId& assetId);
void OnDataSwitch();
void RegisterEntity(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void UnregisterEntity(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier);
void OnChildDataChanged(GraphCanvasTreeItem* treeItem) override;
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState debugging) override final;
GraphPivotTreeEntityItem* FindDynamicallySpawnedTreeItem() const;
GraphPivotTreeEntityItem* FindEntityTreeItem(const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) const;
// LoggingDataNotificationBus
void OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
////
protected:
void OnLoggingDataIdSet() override;
private:
void SetupDynamicallySpawnedElementItem(bool isChecked);
void CalculateCheckState();
Qt::CheckState m_checkState;
AZStd::unordered_multimap< AZ::EntityId, GraphPivotTreeEntityItem*> m_pivotItems;
};
class GraphPivotTreeFolder
: public PivotTreeItem
{
public:
AZ_CLASS_ALLOCATOR(GraphPivotTreeFolder, AZ::SystemAllocator, 0);
AZ_RTTI(GraphPivotTreeRoot, "{E67BBC27-6E0D-4D56-A7D4-9389FE30E909}", PivotTreeItem);
GraphPivotTreeFolder(AZStd::string_view folder);
~GraphPivotTreeFolder() override = default;
Qt::CheckState GetCheckState() const override final;
protected:
void SetCheckState(Qt::CheckState debugging) override final;
AZStd::string GetDisplayName() const override final;
void OnChildDataChanged(GraphCanvasTreeItem* treeItem) override;
private:
void CalculateCheckState();
AZStd::string m_folderName;
Qt::CheckState m_checkState;
};
class GraphPivotTreeRoot
: public PivotTreeRoot
, public LoggingDataNotificationBus::Handler
, public GraphCanvas::CategorizerInterface
, public QObject
{
public:
friend class GraphPivotTreeWidget;
AZ_CLASS_ALLOCATOR(GraphPivotTreeRoot, AZ::SystemAllocator, 0);
AZ_RTTI(GraphPivotTreeRoot, "{B4CD0FCF-F8C7-44D5-BF4D-12A52BB088CB}", PivotTreeRoot);
GraphPivotTreeRoot();
~GraphPivotTreeRoot() override = default;
// PivotTreeRoot
void OnDataSourceChanged(const LoggingDataId& aggregateDataSource) override;
////
// LoggedDataNotifications
void OnDataCaptureBegin() override;
void OnDataCaptureEnd() override;
void OnEntityGraphRegistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
void OnEntityGraphUnregistered(const AZ::NamedEntityId& entityId, const ScriptCanvas::GraphIdentifier& assetId) override;
void OnEnabledStateChanged(bool isEnabled, const AZ::NamedEntityId& namedEntityId, const ScriptCanvas::GraphIdentifier& graphIdentifier) override;
////
// Category Interface
GraphCanvas::GraphCanvasTreeItem* CreateCategoryNode(AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvasTreeItem* parent) const override;
////
protected:
// Slots to hook up into the asset model
void OnScriptCanvasGraphAssetAdded(const QModelIndex& parentIndex, int first, int last);
void OnScriptCanvasGraphAssetRemoved(const QModelIndex& parentIndex, int first, int last);
////
private:
void ProcessEntry(AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
void TraverseTree(QModelIndex index = QModelIndex());
LoggingDataId m_dataSource;
AZStd::unordered_map< AZ::Data::AssetId, GraphPivotTreeGraphItem* > m_graphTreeItemMapping;
GraphCanvas::GraphCanvasTreeCategorizer m_categorizer;
LoggingDataId m_loggedDataId;
bool m_capturingData;
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_assetModel;
};
class GraphPivotTreeWidget
: public PivotTreeWidget
{
Q_OBJECT
public:
GraphPivotTreeWidget(QWidget* parent);
};
}
@@ -0,0 +1,430 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "precompiled.h"
#include <AzCore/Asset/AssetManagerBus.h>
#include <ScriptCanvas/Bus/RequestBus.h>
#include <Editor/View/Widgets/LoggingPanel/PivotTree/PivotTreeWidget.h>
// Disable warnings in moc code
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <Editor/View/Widgets/LoggingPanel/PivotTree/ui_PivotTreeWidget.h>
AZ_POP_DISABLE_OVERRIDE_WARNING
namespace ScriptCanvasEditor
{
//////////////////
// PivotTreeItem
//////////////////
PivotTreeItem::PivotTreeItem()
: m_isPivotElement(false)
{
}
PivotTreeItem::~PivotTreeItem()
{
}
const LoggingDataId& PivotTreeItem::GetLoggingDataId() const
{
return m_loggingDataId;
}
int PivotTreeItem::GetColumnCount() const
{
return Column::Count;
}
Qt::ItemFlags PivotTreeItem::Flags([[maybe_unused]] const QModelIndex& index) const
{
Qt::ItemFlags flags = Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsSelectable | Qt::ItemFlag::ItemIsUserCheckable;
if (m_isPivotElement)
{
flags |= Qt::ItemFlag::ItemIsAutoTristate;
}
return flags;
}
QVariant PivotTreeItem::Data(const QModelIndex& index, int role) const
{
switch (index.column())
{
case Column::Name:
{
if (role == Qt::DisplayRole
|| role == Qt::ToolTip)
{
return QString(GetDisplayName().c_str());
}
else if (role == Qt::CheckStateRole)
{
return GetCheckState();
}
}
break;
default:
break;
}
return QVariant();
}
bool PivotTreeItem::SetData(const QModelIndex& index, const QVariant& value, int role)
{
switch (index.column())
{
case Column::Name:
{
if (role == Qt::CheckStateRole)
{
Qt::CheckState checkState = value.value<Qt::CheckState>();
// Never want to let the user interaction set it to
if (checkState == Qt::CheckState::PartiallyChecked)
{
if (GetCheckState() == Qt::CheckState::Unchecked)
{
checkState = Qt::CheckState::Checked;
}
else
{
checkState = Qt::CheckState::Unchecked;
}
}
SetCheckState(checkState);
}
}
break;
default:
break;
}
return false;
}
void PivotTreeItem::OnChildAdded(GraphCanvasTreeItem* treeItem)
{
if (m_loggingDataId.IsValid())
{
static_cast<PivotTreeItem*>(treeItem)->SetLoggingDataId(m_loggingDataId);
}
}
void PivotTreeItem::OnLoggingDataIdSet()
{
}
void PivotTreeItem::SetLoggingDataId(const LoggingDataId& dataId)
{
if (dataId != m_loggingDataId)
{
m_loggingDataId = dataId;
for (int i = 0; i < GetChildCount(); ++i)
{
PivotTreeItem* treeItem = static_cast<PivotTreeItem*>(FindChildByRow(i));
treeItem->SetLoggingDataId(dataId);
}
OnLoggingDataIdSet();
}
}
void PivotTreeItem::SetIsPivotedElement(bool isPivotElement)
{
m_isPivotElement = isPivotElement;
}
////////////////////////
// PivotTreeEntityItem
////////////////////////
PivotTreeEntityItem::PivotTreeEntityItem(const AZ::NamedEntityId& namedEntityId)
: m_namedEntityId(namedEntityId)
{
}
const AZ::NamedEntityId& PivotTreeEntityItem::GetNamedEntityId() const
{
return m_namedEntityId;
}
AZStd::string PivotTreeEntityItem::GetDisplayName() const
{
return m_namedEntityId.ToString();
}
const AZ::EntityId& PivotTreeEntityItem::GetEntityId() const
{
return m_namedEntityId;
}
///////////////////////
// PivotTreeGraphItem
///////////////////////
PivotTreeGraphItem::PivotTreeGraphItem(const AZ::Data::AssetId& assetId)
: m_assetId(assetId)
{
// Determine the file name for our asset
AZStd::string fullPath;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(fullPath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, m_assetId);
AZStd::size_t indexOf = fullPath.find_last_of('/');
if (indexOf == AZStd::string::npos)
{
m_assetName = fullPath;
m_assetPath = "";
}
else
{
m_assetPath = fullPath.substr(0, indexOf);
m_assetName = fullPath.substr(indexOf + 1);
}
}
AZStd::string PivotTreeGraphItem::GetDisplayName() const
{
return m_assetName;
}
const AZ::Data::AssetId& PivotTreeGraphItem::GetAssetId() const
{
return m_assetId;
}
AZStd::string_view PivotTreeGraphItem::GetAssetPath() const
{
return m_assetPath;
}
//////////////////
// PivotTreeRoot
//////////////////
void PivotTreeRoot::SwitchDataSource(const LoggingDataId& aggregateDataSource)
{
SetLoggingDataId(aggregateDataSource);
OnDataSourceChanged(aggregateDataSource);
}
Qt::CheckState PivotTreeRoot::GetCheckState() const
{
return Qt::CheckState::Unchecked;
}
void PivotTreeRoot::SetCheckState(Qt::CheckState checkState)
{
AZ_UNUSED(checkState);
}
AZStd::string PivotTreeRoot::GetDisplayName() const
{
return "";
}
////////////////////////////
// PivotTreeSortProxyModel
////////////////////////////
bool PivotTreeSortProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
if (m_filter.isEmpty())
{
return true;
}
QAbstractItemModel* model = sourceModel();
QModelIndex index = model->index(sourceRow, PivotTreeItem::Column::Name, sourceParent);
PivotTreeItem* basePivotTreeItem = static_cast<PivotTreeItem*>(index.internalPointer());
QString test = model->data(index, Qt::DisplayRole).toString();
bool showRow = test.lastIndexOf(m_filterRegex) >= 0;
// Handle showing ourselves if a child is being displayed
if (!showRow && sourceModel()->hasChildren(index))
{
for (int i = 0; i < sourceModel()->rowCount(index); ++i)
{
if (filterAcceptsRow(i, index))
{
showRow = true;
break;
}
}
}
// We also want to display ourselves if any of our parents match the filter
QModelIndex parentIndex = sourceModel()->parent(index);
while (!showRow && parentIndex.isValid())
{
QString test2 = model->data(parentIndex).toString();
showRow = test2.contains(m_filterRegex);
parentIndex = sourceModel()->parent(parentIndex);
}
return showRow;
}
bool PivotTreeSortProxyModel::HasFilter() const
{
return !m_filter.isEmpty();
}
void PivotTreeSortProxyModel::SetFilter(const QString& filter)
{
m_filter = filter;
m_filterRegex = QRegExp(m_filter, Qt::CaseInsensitive);
invalidateFilter();
}
void PivotTreeSortProxyModel::ClearFilter()
{
if (HasFilter())
{
SetFilter("");
}
}
////////////////////
// PivotTreeWidget
////////////////////
PivotTreeWidget::PivotTreeWidget(PivotTreeRoot* pivotRoot, const AZ::Crc32& savingId, QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::PivotTreeWidget())
{
m_ui->setupUi(this);
m_pivotRoot = pivotRoot;
m_treeModel = aznew GraphCanvas::GraphCanvasTreeModel(pivotRoot);
m_proxyModel = aznew PivotTreeSortProxyModel();
m_proxyModel->ClearFilter();
m_proxyModel->setSourceModel(m_treeModel);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
m_ui->pivotTreeView->setModel(m_proxyModel);
m_ui->pivotTreeView->sortByColumn(PivotTreeItem::Column::Name, Qt::SortOrder::AscendingOrder);
m_ui->pivotTreeView->header()->setHidden(true);
m_ui->pivotTreeView->header()->setStretchLastSection(false);
m_ui->pivotTreeView->header()->setSectionResizeMode(PivotTreeItem::Column::Name, QHeaderView::ResizeMode::Stretch);
m_ui->pivotTreeView->header()->setSectionResizeMode(PivotTreeItem::Column::QT_NEEDS_A_SECOND_COLUMN_FOR_THIS_MODEL_TO_WORK_FOR_SOME_REASON, QHeaderView::ResizeMode::Fixed);
m_ui->pivotTreeView->header()->resizeSection(PivotTreeItem::Column::QT_NEEDS_A_SECOND_COLUMN_FOR_THIS_MODEL_TO_WORK_FOR_SOME_REASON, 1);
QObject::connect(m_ui->filterWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, this, &PivotTreeWidget::OnFilterChanged);
m_ui->filterWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
m_ui->pivotTreeView->InitializeTreeViewSaving(savingId);
m_ui->pivotTreeView->PauseTreeViewSaving();
QObject::connect(m_ui->pivotTreeView, &QTreeView::doubleClicked, this, &PivotTreeWidget::OnItemDoubleClicked);
}
PivotTreeWidget::~PivotTreeWidget()
{
}
void PivotTreeWidget::DisplayTree()
{
OnTreeDisplayed();
}
void PivotTreeWidget::SwitchDataSource(const LoggingDataId& aggregateDataSource)
{
{
QSignalBlocker signalBlocker(m_ui->filterWidget);
m_ui->filterWidget->ClearTextFilter();
OnFilterChanged("");
}
m_pivotRoot->SwitchDataSource(aggregateDataSource);
}
void PivotTreeWidget::OnFilterChanged(const QString& activeTextFilter)
{
bool hadFilter = m_proxyModel->HasFilter();
if (!m_proxyModel->HasFilter() && !activeTextFilter.isEmpty())
{
m_ui->pivotTreeView->UnpauseTreeViewSaving();
m_ui->pivotTreeView->CaptureTreeViewSnapshot();
m_ui->pivotTreeView->PauseTreeViewSaving();
}
m_proxyModel->SetFilter(activeTextFilter);
if (hadFilter && !m_proxyModel->HasFilter())
{
m_ui->pivotTreeView->UnpauseTreeViewSaving();
m_ui->pivotTreeView->ApplyTreeViewSnapshot();
m_ui->pivotTreeView->PauseTreeViewSaving();
}
else if (m_proxyModel->HasFilter())
{
m_ui->pivotTreeView->expandAll();
}
}
PivotTreeRoot* PivotTreeWidget::GetTreeRoot()
{
return m_pivotRoot;
}
void PivotTreeWidget::OnTreeDisplayed()
{
}
void PivotTreeWidget::OnItemDoubleClicked(const QModelIndex& modelIndex)
{
QModelIndex sourceIndex = modelIndex;
QSortFilterProxyModel* proxyModel = qobject_cast<QSortFilterProxyModel*>(m_ui->pivotTreeView->model());
if (proxyModel)
{
sourceIndex = proxyModel->mapToSource(modelIndex);
}
PivotTreeItem* pivotTreeItem = static_cast<PivotTreeItem*>(sourceIndex.internalPointer());
if (pivotTreeItem)
{
PivotTreeGraphItem* graphItem = azrtti_cast<PivotTreeGraphItem*>(pivotTreeItem);
if (graphItem)
{
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAssetId, graphItem->GetAssetId());
}
}
}
#include <Editor/View/Widgets/LoggingPanel/PivotTree/moc_PivotTreeWidget.cpp>
}
@@ -0,0 +1,208 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/PlatformDef.h>
// qbrush.h(118): warning C4251: 'QBrush::d': class 'QScopedPointer<QBrushData,QBrushDataPointerDeleter>' needs to have dll-interface to be used by clients of class 'QBrush'
// qwidget.h(858): warning C4800: 'uint': forcing value to bool 'true' or 'false' (performance warning)
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option")
#include <QTimer>
#include <QTreeView>
#include <QSortFilterProxyModel>
AZ_POP_DISABLE_WARNING
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeModel.h>
#include <Editor/View/Widgets/LoggingPanel/LoggingWindowSession.h>
#endif
namespace Ui
{
class PivotTreeWidget;
}
namespace ScriptCanvasEditor
{
class PivotTreeItem
: public GraphCanvas::GraphCanvasTreeItem
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(PivotTreeItem, "{F310C0EA-9CFE-4A8F-9CDA-46E24673B01A}", GraphCanvas::GraphCanvasTreeItem);
enum Column
{
IndexForce = -1,
Name,
// Seriously. Returning 1 causes the data to only ask for the tool tip.
// No idea why.
QT_NEEDS_A_SECOND_COLUMN_FOR_THIS_MODEL_TO_WORK_FOR_SOME_REASON,
Count
};
PivotTreeItem();
~PivotTreeItem();
const LoggingDataId& GetLoggingDataId() const;
// GraphCanvasTreeItem
int GetColumnCount() const override final;
Qt::ItemFlags Flags(const QModelIndex& index) const override final;
QVariant Data(const QModelIndex& index, int role) const override final;
bool SetData(const QModelIndex& index, const QVariant& value, int role) override final;
void OnChildAdded(GraphCanvasTreeItem* treeItem) override final;
////
virtual Qt::CheckState GetCheckState() const = 0;
virtual void SetCheckState(Qt::CheckState checkState) = 0;
protected:
virtual AZStd::string GetDisplayName() const = 0;
virtual void OnLoggingDataIdSet();
void SetLoggingDataId(const LoggingDataId& dataId);
void SetIsPivotedElement(bool isPivotedElement);
private:
bool m_isPivotElement;
LoggingDataId m_loggingDataId;
};
class PivotTreeEntityItem
: public PivotTreeItem
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeEntityItem, AZ::SystemAllocator, 0);
AZ_RTTI(PivotTreeEntityItem, "{67725865-7004-441D-84BB-D38FF491A3FD}", PivotTreeItem);
PivotTreeEntityItem(const AZ::NamedEntityId& entityId);
const AZ::NamedEntityId& GetNamedEntityId() const;
protected:
AZStd::string GetDisplayName() const override final;
const AZ::EntityId& GetEntityId() const;
private:
AZ::NamedEntityId m_namedEntityId;
};
class PivotTreeGraphItem
: public PivotTreeItem
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeGraphItem, AZ::SystemAllocator, 0);
AZ_RTTI(PivotTreeGraphItem, "{37FCAC77-DE32-4B1B-97FD-66852EC31CAB}", PivotTreeItem);
PivotTreeGraphItem(const AZ::Data::AssetId& assetId);
const AZ::Data::AssetId& GetAssetId() const;
protected:
AZStd::string GetDisplayName() const override final;
AZStd::string_view GetAssetPath() const;
private:
AZ::Data::AssetId m_assetId;
AZStd::string m_assetPath;
AZStd::string m_assetName;
};
class PivotTreeRoot
: public PivotTreeItem
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeRoot, AZ::SystemAllocator, 0);
AZ_RTTI(PivotTreeRoot, "{E172AB89-49BA-429F-AC83-9CCBD6A3B1B9}", PivotTreeItem);
PivotTreeRoot() = default;
void SwitchDataSource(const LoggingDataId& aggregateDataSource);
protected:
Qt::CheckState GetCheckState() const override final;
void SetCheckState(Qt::CheckState checkState) override final;
AZStd::string GetDisplayName() const override final;
virtual void OnDataSourceChanged(const LoggingDataId& aggregateDataSource) = 0;
private:
LoggingDataId m_loggingDataId;
};
class PivotTreeSortProxyModel
: public QSortFilterProxyModel
{
public:
AZ_CLASS_ALLOCATOR(PivotTreeSortProxyModel, AZ::SystemAllocator, 0);
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override;
bool HasFilter() const;
void SetFilter(const QString& filter);
void ClearFilter();
private:
QString m_filter;
QRegExp m_filterRegex;
};
class PivotTreeWidget
: public QWidget
{
Q_OBJECT
public:
~PivotTreeWidget();
void DisplayTree();
void SwitchDataSource(const LoggingDataId& aggregateDataSource);
public Q_SLOT:
void OnFilterChanged(const QString& activeTextFilter);
protected:
PivotTreeWidget(PivotTreeRoot* pivotRoot, const AZ::Crc32& savingId, QWidget* parent);
PivotTreeRoot* GetTreeRoot();
virtual void OnTreeDisplayed();
private:
void OnItemDoubleClicked(const QModelIndex& modelIndex);
AZStd::unique_ptr<Ui::PivotTreeWidget> m_ui;
PivotTreeRoot* m_pivotRoot;
GraphCanvas::GraphCanvasTreeModel* m_treeModel;
PivotTreeSortProxyModel* m_proxyModel;
};
}
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PivotTreeWidget</class>
<widget class="QWidget" name="PivotTreeWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>372</width>
<height>476</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>5</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="filterWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="AzToolsFramework::QTreeViewWithStateSaving" name="pivotTreeView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="expandsOnDoubleClick">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::FilteredSearchWidget</class>
<extends>QWidget</extends>
<header location="global">AzQtComponents/Components/FilteredSearchWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AzToolsFramework::QTreeViewWithStateSaving</class>
<extends>QTreeView</extends>
<header location="global">AzToolsFramework/UI/UICore/QTreeViewStateSaver.hxx</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>