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,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 <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <Editor/Assets/ScriptCanvasAssetTrackerBus.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <Editor/View/Widgets/StatisticsDialog/NodeUsageTreeItem.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <ScriptCanvas/Components/EditorGraph.h>
namespace ScriptCanvasEditor
{
/////////////////////////////////
// NodePaletteNodeUsageRootItem
/////////////////////////////////
NodePaletteNodeUsageRootItem::NodePaletteNodeUsageRootItem(const NodePaletteModel& nodePaletteModel)
: GraphCanvas::NodePaletteTreeItem("root", ScriptCanvasEditor::AssetEditorId)
, m_nodePaletteModel(nodePaletteModel)
, m_categorizer(nodePaletteModel)
{
}
NodePaletteNodeUsageRootItem::~NodePaletteNodeUsageRootItem()
{
}
GraphCanvas::NodePaletteTreeItem* NodePaletteNodeUsageRootItem::GetCategoryNode(const char* categoryPath, GraphCanvas::NodePaletteTreeItem* parentRoot)
{
if (parentRoot)
{
return static_cast<GraphCanvas::NodePaletteTreeItem*>(m_categorizer.GetCategoryNode(categoryPath, parentRoot));
}
else
{
return static_cast<GraphCanvas::NodePaletteTreeItem*>(m_categorizer.GetCategoryNode(categoryPath, this));
}
}
void NodePaletteNodeUsageRootItem::PruneEmptyNodes()
{
m_categorizer.PruneEmptyNodes();
}
////////////////////////////////////
// NodePaletteNodeUsagePaletteItem
////////////////////////////////////
NodePaletteNodeUsagePaletteItem::NodePaletteNodeUsagePaletteItem(const ScriptCanvas::NodeTypeIdentifier& nodeIdentifier, AZStd::string_view displayName)
: GraphCanvas::IconDecoratedNodePaletteTreeItem(displayName, ScriptCanvasEditor::AssetEditorId)
, m_nodeIdentifier(nodeIdentifier)
{
}
NodePaletteNodeUsagePaletteItem::~NodePaletteNodeUsagePaletteItem()
{
}
const ScriptCanvas::NodeTypeIdentifier& NodePaletteNodeUsagePaletteItem::GetNodeTypeIdentifier() const
{
return m_nodeIdentifier;
}
//////////////////////////////
// ScriptCanvasAssetTreeItem
//////////////////////////////
ScriptCanvasAssetNodeUsageTreeItem::ScriptCanvasAssetNodeUsageTreeItem(AZStd::string_view assetName)
: m_name(QString::fromUtf8(assetName.data(), static_cast<int>(assetName.size())))
, m_icon(":/ScriptCanvasEditorResources/Resources/edit_icon.png")
, m_activeIdentifier(0)
{
}
int ScriptCanvasAssetNodeUsageTreeItem::GetColumnCount() const
{
return Column::Count;
}
QVariant ScriptCanvasAssetNodeUsageTreeItem::Data(const QModelIndex& index, int role) const
{
if (index.column() == Column::Name)
{
switch (role)
{
case Qt::DisplayRole:
return GetName();
case Qt::DecorationRole:
break;
default:
break;
}
}
else if (index.column() == Column::UsageCount && m_assetId.IsValid())
{
switch (role)
{
case Qt::DisplayRole:
return GetNodeCount();
default:
break;
}
}
else if (index.column() == Column::OpenIcon)
{
if (m_assetId.IsValid())
{
switch (role)
{
case Qt::DecorationRole:
return m_icon;
default:
break;
}
}
}
return QVariant();
}
Qt::ItemFlags ScriptCanvasAssetNodeUsageTreeItem::Flags([[maybe_unused]] const QModelIndex& index) const
{
Qt::ItemFlags baseFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return baseFlags;
}
void ScriptCanvasAssetNodeUsageTreeItem::SetAssetId(const AZ::Data::AssetId& assetId)
{
// If we are setting up a new assetId, we wantt o register for the bus.
// Otherwise we just want to reload the asset to scrape some data from it.
if (m_assetId != assetId)
{
if (AZ::Data::AssetBus::Handler::BusIsConnected())
{
AZ::Data::AssetBus::Handler::BusDisconnect();
}
m_assetId = assetId;
AZ::Data::AssetBus::Handler::BusConnect(assetId);
}
AZ::Data::Asset<ScriptCanvasAsset> newAsset;
const bool loadBlocking = false;
auto onAssetReady = [](ScriptCanvasMemoryAsset&) {};
AssetTrackerRequestBus::Broadcast(&AssetTrackerRequests::Load, m_assetId, azrtti_typeid<ScriptCanvasAsset>(), onAssetReady);
ProcessAsset(newAsset);
}
const AZ::Data::AssetId& ScriptCanvasAssetNodeUsageTreeItem::GetAssetId() const
{
return m_assetId;
}
const QString& ScriptCanvasAssetNodeUsageTreeItem::GetName() const
{
return m_name;
}
void ScriptCanvasAssetNodeUsageTreeItem::SetActiveNodeType(const ScriptCanvas::NodeTypeIdentifier& nodeTypeIdentifier)
{
if (m_activeIdentifier != nodeTypeIdentifier)
{
m_activeIdentifier = nodeTypeIdentifier;
SignalDataChanged();
}
}
int ScriptCanvasAssetNodeUsageTreeItem::GetNodeCount() const
{
auto nodeIter = m_statisticsHelper.m_nodeIdentifierCount.find(m_activeIdentifier);
if (nodeIter == m_statisticsHelper.m_nodeIdentifierCount.end())
{
return 0;
}
return nodeIter->second;
}
void ScriptCanvasAssetNodeUsageTreeItem::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
ProcessAsset(asset);
}
void ScriptCanvasAssetNodeUsageTreeItem::OnAssetSaved(AZ::Data::Asset<AZ::Data::AssetData> asset, bool isSuccessful)
{
if (isSuccessful)
{
ProcessAsset(asset);
}
}
void ScriptCanvasAssetNodeUsageTreeItem::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
ProcessAsset(asset);
}
void ScriptCanvasAssetNodeUsageTreeItem::ProcessAsset(const AZ::Data::Asset<ScriptCanvasAsset>& scriptCanvasAsset)
{
if (scriptCanvasAsset.IsReady())
{
AZ::Entity* scriptCanvasEntity = scriptCanvasAsset.Get()->GetScriptCanvasEntity();
Graph* editorGraph = AZ::EntityUtils::FindFirstDerivedComponent<Graph>(scriptCanvasEntity);
if (editorGraph)
{
m_statisticsHelper = editorGraph->GetNodeUsageStatistics();
// Temporary measure to deal potentially unfilled out data.
if (m_statisticsHelper.m_nodeIdentifierCount.empty())
{
m_statisticsHelper.PopulateStatisticData(editorGraph);
}
SignalDataChanged();
}
}
}
///////////////////////////////////////////
// ScriptCanvasAssetNodeUsageTreeItemRoot
///////////////////////////////////////////
ScriptCanvasAssetNodeUsageTreeItemRoot::ScriptCanvasAssetNodeUsageTreeItemRoot()
: ScriptCanvasAssetNodeUsageTreeItem("root")
, m_categorizer((*this))
{
}
void ScriptCanvasAssetNodeUsageTreeItemRoot::RegisterAsset(const AZ::Data::AssetId& assetId)
{
auto asset = AZ::Data::AssetManager::Instance().GetAsset(assetId, azrtti_typeid<ScriptCanvasAsset>(), AZ::Data::AssetLoadBehavior::Default);
if (!asset.IsReady())
{
// The asset must be loaded before it an be registered. We will connect to the AssetBus and wait for it to be ready.
AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
return;
}
auto treeItem = GetAssetItem(assetId);
if (treeItem == nullptr)
{
AZ::Data::AssetInfo assetInfo;
const AZStd::string platformName = ""; // Empty for default
AZStd::string rootFilePath;
bool foundPath = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundPath, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetInfoById, assetId, azrtti_typeid<ScriptCanvasAsset>(), platformName, assetInfo, rootFilePath);
if (foundPath)
{
AZStd::string relativePath = assetInfo.m_relativePath;
AZStd::string fileName;
if (AzFramework::StringFunc::Path::GetFileName(relativePath.c_str(), fileName))
{
AzFramework::StringFunc::Path::Normalize(relativePath);
const bool stripLastComponent = true;
if (AzFramework::StringFunc::Path::StripComponent(relativePath, stripLastComponent))
{
AzFramework::StringFunc::Replace(relativePath, AZ_CORRECT_FILESYSTEM_SEPARATOR, '/');
GraphCanvas::GraphCanvasTreeItem* treeItem2 = m_categorizer.GetCategoryNode(relativePath.c_str(), this);
if (treeItem2)
{
ScriptCanvasAssetNodeUsageTreeItem* usageTreeItem = treeItem2->CreateChildNode<ScriptCanvasAssetNodeUsageTreeItem>(fileName);
usageTreeItem->SetAssetId(assetId);
m_scriptCanvasAssetItems[assetId] = usageTreeItem;
}
}
}
}
}
else
{
treeItem->SetAssetId(assetId);
}
}
void ScriptCanvasAssetNodeUsageTreeItemRoot::RemoveAsset(const AZ::Data::AssetId& assetId)
{
auto assetIter = m_scriptCanvasAssetItems.find(assetId);
if (assetIter != m_scriptCanvasAssetItems.end())
{
assetIter->second->DetachItem();
delete assetIter->second;
m_categorizer.PruneEmptyNodes();
}
}
ScriptCanvasAssetNodeUsageTreeItem* ScriptCanvasAssetNodeUsageTreeItemRoot::GetAssetItem(const AZ::Data::AssetId& assetId)
{
auto assetIter = m_scriptCanvasAssetItems.find(assetId);
if (assetIter != m_scriptCanvasAssetItems.end())
{
return assetIter->second;
}
return nullptr;
}
GraphCanvas::GraphCanvasTreeItem* ScriptCanvasAssetNodeUsageTreeItemRoot::CreateCategoryNode([[maybe_unused]] AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvasTreeItem* parent) const
{
return parent->CreateChildNode<ScriptCanvasAssetNodeUsageTreeItem>(categoryName);
}
const ScriptCanvasAssetNodeUsageTreeItemRoot::ScriptCanvasAssetMap& ScriptCanvasAssetNodeUsageTreeItemRoot::GetAssetTreeItems() const
{
return m_scriptCanvasAssetItems;
}
void ScriptCanvasAssetNodeUsageTreeItemRoot::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
AZ::Data::AssetBus::MultiHandler::BusDisconnect(*AZ::Data::AssetBus::GetCurrentBusId());
RegisterAsset(asset.GetId());
}
}
@@ -0,0 +1,155 @@
/*
* 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 <QIcon>
#include <GraphCanvas/Editor/EditorTypes.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeCategorizer.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <GraphCanvas/Widgets/NodePalette/TreeItems/IconDecoratedNodePaletteTreeItem.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <ScriptCanvas/Components/EditorUtils.h>
namespace ScriptCanvasEditor
{
// NodePaletteItems
class NodePaletteNodeUsageRootItem
: public GraphCanvas::NodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(NodePaletteNodeUsageRootItem, AZ::SystemAllocator, 0);
AZ_RTTI(NodePaletteNodeUsageRootItem, "{ED21874C-6955-40F0-B451-F5FF5A16CF71}", GraphCanvas::NodePaletteTreeItem);
NodePaletteNodeUsageRootItem(const NodePaletteModel& nodePaletteModel);
~NodePaletteNodeUsageRootItem();
GraphCanvas::NodePaletteTreeItem* GetCategoryNode(const char* categoryPath, GraphCanvas::NodePaletteTreeItem* parentRoot = nullptr);
void PruneEmptyNodes();
private:
const NodePaletteModel& m_nodePaletteModel;
GraphCanvas::GraphCanvasTreeCategorizer m_categorizer;
};
class NodePaletteNodeUsagePaletteItem
: public GraphCanvas::IconDecoratedNodePaletteTreeItem
{
public:
AZ_CLASS_ALLOCATOR(NodePaletteNodeUsagePaletteItem, AZ::SystemAllocator, 0);
AZ_RTTI(NodePaletteNodeUsagePaletteItem, "{CA8E31A8-56CA-49A2-80F2-68A1E3A9EDF6}", GraphCanvas::IconDecoratedNodePaletteTreeItem);
NodePaletteNodeUsagePaletteItem(const ScriptCanvas::NodeTypeIdentifier& nodeIdentifier, AZStd::string_view displayName);
~NodePaletteNodeUsagePaletteItem();
const ScriptCanvas::NodeTypeIdentifier& GetNodeTypeIdentifier() const;
private:
ScriptCanvas::NodeTypeIdentifier m_nodeIdentifier;
};
////
// General TreeItems
class ScriptCanvasAssetNodeUsageTreeItem
: public GraphCanvas::GraphCanvasTreeItem
, public AZ::Data::AssetBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ScriptCanvasAssetNodeUsageTreeItem, AZ::SystemAllocator, 0);
AZ_RTTI(ScriptCanvasAssetNodeUsageTreeItem, "{1FF437D9-5159-49CD-8D80-8AC3334886E8}", GraphCanvas::GraphCanvasTreeItem);
enum Column
{
IndexForce = -1,
Name,
UsageCount,
OpenIcon,
Count
};
ScriptCanvasAssetNodeUsageTreeItem(AZStd::string_view assetName);
~ScriptCanvasAssetNodeUsageTreeItem() = default;
int GetColumnCount() const override final;
QVariant Data(const QModelIndex& index, int role) const override final;
Qt::ItemFlags Flags(const QModelIndex& index) const override final;
void SetAssetId(const AZ::Data::AssetId& assetId);
const AZ::Data::AssetId& GetAssetId() const;
const QString& GetName() const;
void SetActiveNodeType(const ScriptCanvas::NodeTypeIdentifier& nodeTypeIdentifier);
int GetNodeCount() const;
// AZ::Data::AssetBus::Handler
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
void OnAssetSaved(AZ::Data::Asset<AZ::Data::AssetData> asset, bool isSuccessful) override;
void OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
////
private:
void ProcessAsset(const AZ::Data::Asset<ScriptCanvasAsset>& scriptCanvasAsset);
QString m_name;
QIcon m_icon;
ScriptCanvas::NodeTypeIdentifier m_activeIdentifier;
AZ::Data::AssetId m_assetId;
GraphStatisticsHelper m_statisticsHelper;
};
class ScriptCanvasAssetNodeUsageTreeItemRoot
: public ScriptCanvasAssetNodeUsageTreeItem
, public GraphCanvas::CategorizerInterface
, public AZ::Data::AssetBus::MultiHandler
{
public:
AZ_CLASS_ALLOCATOR(ScriptCanvasAssetNodeUsageTreeItemRoot, AZ::SystemAllocator, 0);
AZ_RTTI(ScriptCanvasAssetNodeUsageTreeItemRoot, "{EDCBFE97-0BF9-4AE5-8C6E-C4805E08CBFC}", ScriptCanvasAssetNodeUsageTreeItem);
typedef AZStd::unordered_map< AZ::Data::AssetId, ScriptCanvasAssetNodeUsageTreeItem* > ScriptCanvasAssetMap;
ScriptCanvasAssetNodeUsageTreeItemRoot();
~ScriptCanvasAssetNodeUsageTreeItemRoot() = default;
void RegisterAsset(const AZ::Data::AssetId& assetId);
void RemoveAsset(const AZ::Data::AssetId& assetId);
ScriptCanvasAssetNodeUsageTreeItem* GetAssetItem(const AZ::Data::AssetId& assetId);
// CategorizerInterface
GraphCanvas::GraphCanvasTreeItem* CreateCategoryNode(AZStd::string_view categoryPath, AZStd::string_view categoryName, GraphCanvasTreeItem* parent) const override;
////
const ScriptCanvasAssetMap& GetAssetTreeItems() const;
private:
// AZ::Data::AssetBus::MultiHandler
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
GraphCanvas::GraphCanvasTreeCategorizer m_categorizer;
ScriptCanvasAssetMap m_scriptCanvasAssetItems;
};
////
}
@@ -0,0 +1,463 @@
/*
* 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 <AzQtComponents/Components/StyleManager.h>
#include <Editor/View/Widgets/StatisticsDialog/ScriptCanvasStatisticsDialog.h>
#include <Editor/View/Widgets/StatisticsDialog/ui_ScriptCanvasStatisticsDialog.h>
#include <Editor/GraphCanvas/GraphCanvasEditorNotificationBusId.h>
#include <ScriptCanvas/Assets/ScriptCanvasAsset.h>
#include <ScriptCanvas/Bus/RequestBus.h>
namespace
{
ScriptCanvasEditor::NodePaletteNodeUsageRootItem* ExternalCreatePaletteRoot(const ScriptCanvasEditor::NodePaletteModel& nodePaletteModel, AZStd::unordered_map< ScriptCanvas::NodeTypeIdentifier, GraphCanvas::GraphCanvasTreeItem* >& leafMap)
{
ScriptCanvasEditor::NodePaletteNodeUsageRootItem* root = aznew ScriptCanvasEditor::NodePaletteNodeUsageRootItem(nodePaletteModel);
const ScriptCanvasEditor::NodePaletteModel::NodePaletteRegistry& nodeRegistry = nodePaletteModel.GetNodeRegistry();
for (const auto& registryPair : nodeRegistry)
{
const ScriptCanvasEditor::NodePaletteModelInformation* modelInformation = registryPair.second;
GraphCanvas::GraphCanvasTreeItem* parentItem = root->GetCategoryNode(modelInformation->m_categoryPath.c_str());
GraphCanvas::NodePaletteTreeItem* createdItem = nullptr;
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::NodePaletteNodeUsagePaletteItem>(modelInformation->m_nodeIdentifier, modelInformation->m_displayName);
if (createdItem)
{
modelInformation->PopulateTreeItem((*createdItem));
leafMap[modelInformation->m_nodeIdentifier] = createdItem;
}
}
root->PruneEmptyNodes();
return root;
}
}
namespace ScriptCanvasEditor
{
//////////////////////////////////////////
// ScriptCanvasAssetNodeUsageFilterModel
//////////////////////////////////////////
ScriptCanvasAssetNodeUsageFilterModel::ScriptCanvasAssetNodeUsageFilterModel()
: m_nodeIdentifier(0)
{
}
bool ScriptCanvasAssetNodeUsageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
// Never want to show something if we don't have a node type identifier
if (m_nodeIdentifier == 0)
{
return false;
}
QAbstractItemModel* model = sourceModel();
QModelIndex index = model->index(sourceRow, 0, sourceParent);
ScriptCanvasAssetNodeUsageTreeItem* treeItem = static_cast<ScriptCanvasAssetNodeUsageTreeItem*>(index.internalPointer());
if (!treeItem->GetAssetId().IsValid())
{
for (int i = 0; i < treeItem->GetChildCount(); ++i)
{
if (filterAcceptsRow(i, index))
{
return true;
}
}
}
else
{
treeItem->SetActiveNodeType(m_nodeIdentifier);
if (treeItem->GetNodeCount() == 0)
{
return false;
}
bool showRow = m_filter.isEmpty();
if (!showRow)
{
const QString& name = treeItem->GetName();
int regexIndex = name.lastIndexOf(m_regex);
showRow = regexIndex >= 0;
if (!showRow)
{
ScriptCanvasAssetNodeUsageTreeItem* parentItem = static_cast<ScriptCanvasAssetNodeUsageTreeItem*>(treeItem->GetParent());
while (!showRow && parentItem)
{
ScriptCanvasAssetNodeUsageTreeItem* nextItem = static_cast<ScriptCanvasAssetNodeUsageTreeItem*>(parentItem->GetParent());
// This means we are the root element. And we don't want to match based on it.
if (nextItem == nullptr)
{
break;
}
const QString& parentName = parentItem->GetName();
int regexIndex2 = parentName.lastIndexOf(m_regex);
if (regexIndex2 >= 0)
{
showRow = true;
}
parentItem = nextItem;
}
}
}
return showRow;
}
return false;
}
void ScriptCanvasAssetNodeUsageFilterModel::SetFilter(const QString& filterName)
{
m_filter = filterName;
m_regex = QRegExp(m_filter, Qt::CaseInsensitive);
invalidate();
}
void ScriptCanvasAssetNodeUsageFilterModel::SetNodeTypeFilter(const ScriptCanvas::NodeTypeIdentifier& nodeType)
{
m_nodeIdentifier = nodeType;
invalidate();
}
/////////////////////
// StatisticsDialog
/////////////////////
StatisticsDialog::StatisticsDialog(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* scriptCanvasAssetBrowserModel, QWidget* widget)
: QDialog(widget)
, m_nodePaletteModel(nodePaletteModel)
, m_ui(new Ui::ScriptCanvasStatisticsDialog())
, m_treeRoot(nullptr)
, m_scriptCanvasAssetBrowserModel(scriptCanvasAssetBrowserModel)
, m_scriptCanvasAssetTreeRoot(nullptr)
, m_scriptCanvasAssetTree(nullptr)
, m_scriptCanvasAssetFilterModel(nullptr)
{
setWindowFlags(Qt::WindowFlags::enum_type::WindowCloseButtonHint);
m_ui->setupUi(this);
AzQtComponents::StyleManager::setStyleSheet(this, QStringLiteral("style:Editor.qss"));
}
StatisticsDialog::~StatisticsDialog()
{
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void StatisticsDialog::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
{
if (m_scriptCanvasAssetTreeRoot)
{
AZ::Data::AssetInfo assetInfo;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetInfo, &AZ::Data::AssetCatalogRequests::GetAssetInfoById, assetId);
if (assetInfo.m_assetId.IsValid())
{
if (assetInfo.m_assetType == azrtti_typeid<ScriptCanvasAsset>()
|| assetInfo.m_assetType == azrtti_typeid<ScriptCanvas::ScriptCanvasFunctionAsset>())
{
m_scriptCanvasAssetTreeRoot->RegisterAsset(assetId);
}
}
}
}
void StatisticsDialog::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
{
OnCatalogAssetChanged(assetId);
}
void StatisticsDialog::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& /*assetInfo*/)
{
// at this point, the asset is gone. You can't search for it in the catalog.
if (m_scriptCanvasAssetTreeRoot)
{
m_scriptCanvasAssetTreeRoot->RemoveAsset(assetId);
}
}
void StatisticsDialog::OnAssetModelRepopulated()
{
ResetModel();
}
void StatisticsDialog::OnAssetNodeAdded(NodePaletteModelInformation* modelInformation)
{
auto leafIter = m_leafMap.find(modelInformation->m_nodeIdentifier);
if (leafIter != m_leafMap.end())
{
// Duplicate Id. Ignore for now.
return;
}
GraphCanvas::GraphCanvasTreeItem* parentItem = m_treeRoot->GetCategoryNode(modelInformation->m_categoryPath.c_str());
GraphCanvas::NodePaletteTreeItem* createdItem = nullptr;
createdItem = parentItem->CreateChildNode<ScriptCanvasEditor::NodePaletteNodeUsagePaletteItem>(modelInformation->m_nodeIdentifier, modelInformation->m_displayName);
if (createdItem)
{
modelInformation->PopulateTreeItem((*createdItem));
m_leafMap[modelInformation->m_nodeIdentifier] = createdItem;
}
else
{
m_treeRoot->PruneEmptyNodes();
}
}
void StatisticsDialog::OnAssetNodeRemoved(NodePaletteModelInformation* modelInformation)
{
auto leafIter = m_leafMap.find(modelInformation->m_nodeIdentifier);
if (leafIter != m_leafMap.end())
{
leafIter->second->DetachItem();
delete leafIter->second;
m_leafMap.erase(leafIter);
m_treeRoot->PruneEmptyNodes();
}
}
void StatisticsDialog::OnScriptCanvasAssetClicked(const QModelIndex& index)
{
if (index.isValid())
{
if (index.column() == ScriptCanvasAssetNodeUsageTreeItem::Column::OpenIcon)
{
QModelIndex sourceIndex = m_scriptCanvasAssetFilterModel->mapToSource(index);
ScriptCanvasAssetNodeUsageTreeItem* treeItem = static_cast<ScriptCanvasAssetNodeUsageTreeItem*>(sourceIndex.internalPointer());
if (treeItem->GetAssetId().IsValid())
{
GeneralRequestBus::Broadcast(&GeneralRequests::OpenScriptCanvasAssetId, treeItem->GetAssetId());
}
}
}
}
void StatisticsDialog::showEvent(QShowEvent* showEvent)
{
InitStatisticsWindow();
QDialog::showEvent(showEvent);
}
void StatisticsDialog::OnSelectionCleared()
{
m_scriptCanvasAssetFilterModel->SetNodeTypeFilter(0);
m_ui->statDisplayName->setText("N/A");
m_ui->totalUsageCount->setText(QString::number(0));
m_ui->uniqueGraphsCount->setText(QString::number(0));
m_ui->averageGraphUsages->setText(QString::number(0));
}
void StatisticsDialog::OnItemSelected(const GraphCanvas::GraphCanvasTreeItem* treeItem)
{
const NodePaletteNodeUsagePaletteItem* usageItem = azrtti_cast<const NodePaletteNodeUsagePaletteItem*>(treeItem);
if (usageItem)
{
m_scriptCanvasAssetFilterModel->SetNodeTypeFilter(usageItem->GetNodeTypeIdentifier());
m_ui->scriptCanvasAssetTree->expandAll();
const ScriptCanvasAssetNodeUsageTreeItemRoot::ScriptCanvasAssetMap& assetMapping = m_scriptCanvasAssetTreeRoot->GetAssetTreeItems();
int totalNodeCount = 0;
int uniqueGraphs = 0;
for (auto itemPair : assetMapping)
{
int nodeCount = itemPair.second->GetNodeCount();
if (nodeCount > 0)
{
totalNodeCount += nodeCount;
uniqueGraphs++;
}
}
m_ui->statDisplayName->setText(usageItem->GetName());
m_ui->totalUsageCount->setText(QString::number(totalNodeCount));
m_ui->uniqueGraphsCount->setText(QString::number(uniqueGraphs));
float averageUses = 0;
if (uniqueGraphs != 0)
{
averageUses = static_cast<float>(totalNodeCount) / static_cast<float>(uniqueGraphs);
}
m_ui->averageGraphUsages->setText(QString::number(averageUses, 'g', 2));
}
else
{
OnSelectionCleared();
}
}
void StatisticsDialog::OnFilterUpdated(const QString& filterText)
{
m_scriptCanvasAssetFilterModel->SetFilter(filterText);
m_ui->scriptCanvasAssetTree->expandAll();
}
void StatisticsDialog::OnScriptCanvasAssetRowsInserted(QModelIndex parentIndex, int first, int last)
{
for (int i = first; i <= last; ++i)
{
QModelIndex modelIndex = m_scriptCanvasAssetBrowserModel->index(first, 0, parentIndex);
QModelIndex sourceIndex = m_scriptCanvasAssetBrowserModel->mapToSource(modelIndex);
const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessAsset(entry);
}
}
void StatisticsDialog::InitStatisticsWindow()
{
if (m_treeRoot == nullptr)
{
m_treeRoot = ExternalCreatePaletteRoot(m_nodePaletteModel, m_leafMap);
GraphCanvas::NodePaletteConfig paletteConfig;
paletteConfig.m_rootTreeItem = m_treeRoot;
paletteConfig.m_editorId = ScriptCanvasEditor::AssetEditorId;
paletteConfig.m_mimeType = "";
paletteConfig.m_isInContextMenu = false;
paletteConfig.m_saveIdentifier = "ScriptCanvas_UsageStatistics";
paletteConfig.m_clearSelectionOnSceneChange = false;
paletteConfig.m_allowArrowKeyNavigation = true;
m_ui->nodePaletteWidget->SetupNodePalette(paletteConfig);
m_scriptCanvasAssetTreeRoot = aznew ScriptCanvasAssetNodeUsageTreeItemRoot();
m_scriptCanvasAssetTree = aznew GraphCanvas::GraphCanvasTreeModel(m_scriptCanvasAssetTreeRoot);
m_scriptCanvasAssetFilterModel = aznew ScriptCanvasAssetNodeUsageFilterModel();
m_scriptCanvasAssetFilterModel->setSourceModel(m_scriptCanvasAssetTree);
TraverseTree();
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
m_ui->scriptCanvasAssetTree->setModel(m_scriptCanvasAssetFilterModel);
m_ui->splitter->setStretchFactor(0, 1);
m_ui->splitter->setStretchFactor(1, 2);
m_ui->searchWidget->SetFilterInputInterval(AZStd::chrono::milliseconds(250));
m_ui->scriptCanvasAssetTree->header()->setSectionResizeMode(ScriptCanvasAssetNodeUsageTreeItem::Column::Name, QHeaderView::ResizeMode::ResizeToContents);
m_ui->scriptCanvasAssetTree->header()->setSectionResizeMode(ScriptCanvasAssetNodeUsageTreeItem::Column::UsageCount, QHeaderView::ResizeMode::Fixed);
m_ui->scriptCanvasAssetTree->header()->resizeSection(ScriptCanvasAssetNodeUsageTreeItem::Column::UsageCount, 30);
QObject::connect(m_ui->scriptCanvasAssetTree, &QTreeView::clicked, this, &StatisticsDialog::OnScriptCanvasAssetClicked);
QObject::connect(m_ui->nodePaletteWidget, &GraphCanvas::NodePaletteWidget::OnSelectionCleared, this, &StatisticsDialog::OnSelectionCleared);
QObject::connect(m_ui->nodePaletteWidget, &GraphCanvas::NodePaletteWidget::OnTreeItemSelected, this, &StatisticsDialog::OnItemSelected);
QObject::connect(m_ui->searchWidget, &AzQtComponents::FilteredSearchWidget::TextFilterChanged, this, &StatisticsDialog::OnFilterUpdated);
QObject::connect(m_scriptCanvasAssetBrowserModel, &QAbstractItemModel::rowsInserted, this, &StatisticsDialog::OnScriptCanvasAssetRowsInserted);
OnSelectionCleared();
NodePaletteModelNotificationBus::Handler::BusConnect(m_nodePaletteModel.GetNotificationId());
}
}
void StatisticsDialog::ResetModel()
{
if (m_treeRoot)
{
m_leafMap.clear();
m_treeRoot = ExternalCreatePaletteRoot(m_nodePaletteModel, m_leafMap);
m_ui->nodePaletteWidget->ResetModel(m_treeRoot);
}
}
void StatisticsDialog::TraverseTree(QModelIndex index)
{
QModelIndex sourceIndex = m_scriptCanvasAssetBrowserModel->mapToSource(index);
AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry = reinterpret_cast<AzToolsFramework::AssetBrowser::AssetBrowserEntry*>(sourceIndex.internalPointer());
ProcessAsset(entry);
int rowCount = m_scriptCanvasAssetBrowserModel->rowCount(index);
for (int i = 0; i < rowCount; ++i)
{
QModelIndex nextIndex = m_scriptCanvasAssetBrowserModel->index(i, 0, index);
TraverseTree(nextIndex);
}
}
void StatisticsDialog::ProcessAsset(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry)
{
if (entry)
{
if (entry->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Product)
{
const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry* productEntry = static_cast<const AzToolsFramework::AssetBrowser::ProductAssetBrowserEntry*>(entry);
if (productEntry->GetAssetType() == azrtti_typeid<ScriptCanvasAsset>())
{
const AZ::Data::AssetId& assetId = productEntry->GetAssetId();
m_scriptCanvasAssetTreeRoot->RegisterAsset(assetId);
}
}
}
}
#include <Editor/View/Widgets/StatisticsDialog/moc_ScriptCanvasStatisticsDialog.cpp>
}
@@ -0,0 +1,123 @@
/*
* 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 <QAbstractItemModel>
#include <QDialog>
#include <QSortFilterProxyModel>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/Search/Filter.h>
#include <AzQtComponents/Components/StyledDockWidget.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeModel.h>
#include <GraphCanvas/Widgets/GraphCanvasTreeItem.h>
#include <Editor/View/Widgets/NodePalette/NodePaletteModel.h>
#include <Editor/View/Widgets/StatisticsDialog/NodeUsageTreeItem.h>
#include <ScriptCanvas/Core/Core.h>
#endif
namespace Ui
{
class ScriptCanvasStatisticsDialog;
}
namespace ScriptCanvasEditor
{
class ScriptCanvasAssetNodeUsageFilterModel
: public QSortFilterProxyModel
{
public:
AZ_CLASS_ALLOCATOR(ScriptCanvasAssetNodeUsageFilterModel, AZ::SystemAllocator, 0);
ScriptCanvasAssetNodeUsageFilterModel();
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const;
void SetFilter(const QString& filterName);
void SetNodeTypeFilter(const ScriptCanvas::NodeTypeIdentifier& nodeType);
private:
QString m_filter;
QRegExp m_regex;
ScriptCanvas::NodeTypeIdentifier m_nodeIdentifier;
};
class StatisticsDialog
: public QDialog
, AzFramework::AssetCatalogEventBus::Handler
, public NodePaletteModelNotificationBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(StatisticsDialog, AZ::SystemAllocator, 0);
StatisticsDialog(const NodePaletteModel& nodePaletteModel, AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* scriptCanvasAssetModel, QWidget* widget = nullptr);
~StatisticsDialog();
void InitStatisticsWindow();
void ResetModel();
// AssetSystemBus
//! Called by the AssetCatalog when an asset has been modified
void OnCatalogAssetChanged(const AZ::Data::AssetId& assetId) override;
void OnCatalogAssetAdded(const AZ::Data::AssetId& /*assetId*/) override;
//! Called by the AssetProcessor when an asset in the cache has been removed.
void OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, const AZ::Data::AssetInfo& assetInfo) override;
////
// NodePaletteModelNotificationBus
void OnAssetModelRepopulated() override;
void OnAssetNodeAdded(NodePaletteModelInformation* modelInformation) override;
void OnAssetNodeRemoved(NodePaletteModelInformation* modelInformation) override;
////
void OnScriptCanvasAssetClicked(const QModelIndex& modelIndex);
void showEvent(QShowEvent* showEvent) override;
public slots:
void OnSelectionCleared();
void OnItemSelected(const GraphCanvas::GraphCanvasTreeItem* treeItem);
void OnFilterUpdated(const QString& filterText);
void OnScriptCanvasAssetRowsInserted(QModelIndex modelIndex, int first, int last);
private:
void TraverseTree(QModelIndex index = QModelIndex());
void ProcessAsset(const AzToolsFramework::AssetBrowser::AssetBrowserEntry* entry);
const NodePaletteModel& m_nodePaletteModel;
AZStd::unique_ptr<Ui::ScriptCanvasStatisticsDialog> m_ui;
NodePaletteNodeUsageRootItem* m_treeRoot;
AzToolsFramework::AssetBrowser::AssetBrowserFilterModel* m_scriptCanvasAssetBrowserModel;
ScriptCanvasAssetNodeUsageTreeItemRoot* m_scriptCanvasAssetTreeRoot;
GraphCanvas::GraphCanvasTreeModel* m_scriptCanvasAssetTree;
ScriptCanvasAssetNodeUsageFilterModel* m_scriptCanvasAssetFilterModel;
AZStd::unordered_map< ScriptCanvas::NodeTypeIdentifier, GraphCanvas::GraphCanvasTreeItem* > m_leafMap;
};
}
@@ -0,0 +1,583 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScriptCanvasStatisticsDialog</class>
<widget class="QDialog" name="ScriptCanvasStatisticsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>999</width>
<height>626</height>
</rect>
</property>
<property name="windowTitle">
<string>Script Canvas Statistics</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<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="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="nodeUsageStatistics">
<attribute name="title">
<string>Node Usage Statistics</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<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">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<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="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="opaqueResize">
<bool>true</bool>
</property>
<property name="childrenCollapsible">
<bool>true</bool>
</property>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<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="GraphCanvas::NodePaletteWidget" name="nodePaletteWidget" native="true">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</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_4">
<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_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>4</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="AzQtComponents::FilteredSearchWidget" name="searchWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="scriptCanvasAssetTree">
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_9">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>3</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="QLabel" name="label_3">
<property name="text">
<string>General Stats for</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statDisplayName">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Something Something Something</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</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_7">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>24</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_6">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<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="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Total Uses:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="totalUsageCount">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>145</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>15</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Unique Graph Usages:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="uniqueGraphsCount">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>15</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Average Usage Per Graph:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="averageGraphUsages">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>2</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="indent">
<number>0</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_7">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<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>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_8">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<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>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</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>AzQtComponents::TabWidget</class>
<extends>QTabWidget</extends>
<header location="global">AzQtComponents/Components/Widgets/TabWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GraphCanvas::NodePaletteWidget</class>
<extends>QWidget</extends>
<header location="global">StaticLib/GraphCanvas/Widgets/NodePalette/NodePaletteWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>