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,59 @@
#
# 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.
#
if (NOT PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
ly_add_target(
NAME SceneUI SHARED
NAMESPACE AZ
AUTOMOC
AUTOUIC
AUTORCC
FILES_CMAKE
SceneUI_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
.
PUBLIC
../..
COMPILE_DEFINITIONS
PRIVATE
SCENE_UI_EXPORTS
BUILD_DEPENDENCIES
PRIVATE
AZ::AzCore
AZ::AzFramework
AZ::AzToolsFramework
AZ::SceneCore
AZ::SceneData
PUBLIC
3rdParty::Qt::Core
3rdParty::Qt::Gui
3rdParty::Qt::Widgets
)
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
ly_add_target(
NAME SceneUI.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE AZ
FILES_CMAKE
SceneUI_testing_files.cmake
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTest
AZ::SceneUI
)
ly_add_googletest(
NAME AZ::SceneUI.Tests
)
endif()
@@ -0,0 +1,61 @@
/*
* 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 <SceneAPI/SceneUI/CommonWidgets/ExpandCollapseToggler.h>
#include <QPainter>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
ExpandCollapseToggler::ExpandCollapseToggler(QWidget* parent)
: QAbstractButton(parent)
, m_expandActionImage(":/SceneUI/Common/ExpandIcon.png")
, m_collapseActionImage(":/SceneUI/Common/CollapseIcon.png")
{
setCheckable(true);
connect(this, &ExpandCollapseToggler::toggled, this, &ExpandCollapseToggler::ExpandedChanged);
}
void ExpandCollapseToggler::SetExpanded(bool isExpanded)
{
setChecked(isExpanded);
}
bool ExpandCollapseToggler::IsExpanded() const
{
return isChecked();
}
const QImage* ExpandCollapseToggler::CurrentTargetImage() const
{
return IsExpanded() ? &m_collapseActionImage : &m_expandActionImage;
}
QSize ExpandCollapseToggler::sizeHint() const
{
return CurrentTargetImage()->size();
}
void ExpandCollapseToggler::paintEvent([[maybe_unused]] QPaintEvent* evt)
{
QPainter painter(this);
const QImage* target = CurrentTargetImage();
painter.drawImage(QPoint(0,0), *target);
}
} // SceneUI
} // SceneAPI
} // AZ
#include <CommonWidgets/moc_ExpandCollapseToggler.cpp>
@@ -0,0 +1,55 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QAbstractButton>
#include <QImage>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
class QPaintEvent;
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
/**
ExpandCollapseToggler - Button that shows expand & collapse images and aliases
"checked" state & signals with "Expanded" functions & signals
**/
class SCENE_UI_API ExpandCollapseToggler : public QAbstractButton
{
Q_OBJECT
public:
explicit ExpandCollapseToggler(QWidget* parent = nullptr);
~ExpandCollapseToggler() override = default;
void SetExpanded(bool isExpanded);
bool IsExpanded() const;
signals:
void ExpandedChanged(bool isExpanded);
protected:
QSize sizeHint() const override;
void paintEvent(QPaintEvent* evt) override;
const QImage* CurrentTargetImage() const;
protected:
QImage m_expandActionImage;
QImage m_collapseActionImage;
};
} // SceneUI
} // SceneAPI
} // AZ
@@ -0,0 +1,101 @@
/*
* 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 <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h> // for AssetSystemJobRequestBus
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneUI/CommonWidgets/JobWatcher.h>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
const int JobWatcher::s_jobQueryInterval = 750; // ms
JobWatcher::JobWatcher(const AZStd::string& sourceAssetFullPath, Uuid traceTag)
: m_jobQueryTimer(new QTimer(this))
, m_sourceAssetFullPath(sourceAssetFullPath)
, m_traceTag(traceTag)
{
connect(m_jobQueryTimer, &QTimer::timeout, this, &JobWatcher::OnQueryJobs);
}
void JobWatcher::StartMonitoring()
{
m_jobQueryTimer->start(s_jobQueryInterval);
}
void JobWatcher::OnQueryJobs()
{
using namespace AzToolsFramework;
using namespace AzToolsFramework::AssetSystem;
AZ_TraceContext("Tag", m_traceTag);
// Query for the relevant jobs
Outcome<AssetSystem::JobInfoContainer> result = Failure();
AssetSystemJobRequestBus::BroadcastResult(result, &AssetSystemJobRequestBus::Events::GetAssetJobsInfo, m_sourceAssetFullPath, true);
if (!result.IsSuccess())
{
m_jobQueryTimer->stop();
emit JobQueryFailed("Failed to retrieve job information from Asset Processor.");
return;
}
JobInfoContainer& allJobs = result.GetValue();
if (allJobs.empty())
{
m_jobQueryTimer->stop();
emit JobQueryFailed("Queued file didn't produce any jobs.");
return;
}
bool allFinished = true;
for (const JobInfo& job : allJobs)
{
AZ_Assert(job.m_status != JobStatus::Any,
"The 'Any' status for job should be exclusive to the database and never be a result to a query.");
if (job.m_status != JobStatus::Queued && job.m_status != JobStatus::InProgress)
{
if (AZStd::find(m_reportedJobs.begin(), m_reportedJobs.end(), job.m_jobRunKey) == m_reportedJobs.end())
{
bool wasSuccessful = job.m_status == AzToolsFramework::AssetSystem::JobStatus::Completed;
Outcome<AZStd::string> logFetchResult = Failure();
AssetSystemJobRequestBus::BroadcastResult(logFetchResult, &AssetSystemJobRequestBus::Events::GetJobLog, job.m_jobRunKey);
emit JobProcessingComplete(job.m_platform, job.m_jobRunKey, wasSuccessful,
logFetchResult.IsSuccess() ? logFetchResult.GetValue() : "");
m_reportedJobs.push_back(job.m_jobRunKey);
}
}
else
{
allFinished = false;
}
}
if (allFinished)
{
m_jobQueryTimer->stop();
emit AllJobsComplete();
}
}
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
#include <CommonWidgets/moc_JobWatcher.cpp>
@@ -0,0 +1,66 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QObject>
#include <QTimer>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
namespace AzToolsFramework
{
namespace AssetSystem
{
struct JobInfo;
}
}
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
class SCENE_UI_API JobWatcher : public QObject
{
Q_OBJECT
public:
explicit JobWatcher(const AZStd::string& sourceAssetFullPath, Uuid traceTag);
void StartMonitoring();
signals:
void JobQueryFailed(const char* message);
void JobProcessingComplete(const AZStd::string& platform, u64 jobId, bool success, const AZStd::string& fullLog);
void AllJobsComplete();
private slots:
void OnQueryJobs();
private:
static const int s_jobQueryInterval;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZStd::vector<u64> m_reportedJobs;
AZStd::string m_sourceAssetFullPath;
QTimer* m_jobQueryTimer;
Uuid m_traceTag;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,40 @@
#pragma once
/*
* 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 <AzQtComponents/Components/Widgets/OverlayWidget.h>
#include <AzCore/Memory/SystemAllocator.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
// left in for backwards compatibility with original SceneAPI code
using OverlayWidgetButton = AzQtComponents::OverlayWidgetButton;
// left in for backwards compatibility with original SceneAPI code
using OverlayWidgetButtonList = AzQtComponents::OverlayWidgetButtonList;
// left in for backwards compatibility with original SceneAPI code
class OverlayWidget : public AzQtComponents::OverlayWidget
{
public:
AZ_CLASS_ALLOCATOR(OverlayWidget, SystemAllocator, 0)
using AzQtComponents::OverlayWidget::OverlayWidget;
};
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,33 @@
#pragma once
/*
* 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 <AzQtComponents/Components/Widgets/OverlayWidgetLayer.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
// left in for backwards compatibility with original SceneAPI code
class OverlayWidgetLayer : public AzQtComponents::OverlayWidgetLayer
{
public:
AZ_CLASS_ALLOCATOR(OverlayWidgetLayer, SystemAllocator, 0)
using AzQtComponents::OverlayWidgetLayer::OverlayWidgetLayer;
};
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::UI::OverlayWidgetLayer</class>
<widget class="QWidget" name="AZ::SceneAPI::UI::OverlayWidgetLayer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>64</width>
<height>75</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<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 row="0" column="0">
<widget class="QFrame" name="m_center">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="m_centerLayout">
<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 row="1" column="0">
<widget class="QFrame" name="m_controls">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>30</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="m_controlsLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<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>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,432 @@
/*
* 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 <ctime>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzQtComponents/Components/StyledBusyLabel.h>
#include <AzQtComponents/Components/StyledDetailsTableView.h>
#include <AzToolsFramework/Debug/TraceContextLogFormatter.h>
#include <AzToolsFramework/UI/Logging/LogEntry.h>
#include <CommonWidgets/ui_ProcessingOverlayWidget.h>
#include <SceneAPI/SceneUI/CommonWidgets/ProcessingOverlayWidget.h>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
#include <SceneAPI/SceneUI/Handlers/ProcessingHandlers/ProcessingHandler.h>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
#include <QCloseEvent>
#include <QDateTime>
#include <QLabel>
#include <QTimer>
#include <QHeaderView>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
namespace Internal
{
QtWebEngineMessageFilter::QtWebEngineMessageFilter(QObject* parent)
: QSortFilterProxyModel(parent)
{
}
QtWebEngineMessageFilter::~QtWebEngineMessageFilter()
{
}
bool QtWebEngineMessageFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
auto tableModel = qobject_cast<AzQtComponents::StyledDetailsTableModel*>(sourceModel());
if (!tableModel)
{
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
const int sourceColumn = tableModel->GetColumnIndex(QStringLiteral("message"));
const QModelIndex index = tableModel->index(sourceRow, sourceColumn, sourceParent);
const QVariant data = tableModel->data(index);
static const QString filteredMessage = QStringLiteral("Qt WebEngine seems to be initialized from a plugin. Please set Qt::AA_ShareOpenGLContexts using QCoreApplication::setAttribute before constructing QGuiApplication.");
if (data.toString() == filteredMessage)
{
return false;
}
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
}
ProcessingOverlayWidget::ProcessingOverlayWidget(UI::OverlayWidget* overlay, Layout layout, Uuid traceTag)
: QWidget()
, m_traceTag(traceTag)
, ui(new Ui::ProcessingOverlayWidget())
, m_overlay(overlay)
, m_progressLabel(nullptr)
, m_layerId(UI::OverlayWidget::s_invalidOverlayIndex)
, m_isProcessingComplete(false)
, m_isClosingBlocked(false)
, m_autoCloseOnSuccess(false)
, m_encounteredIssues(false)
, m_resizeTimer(new QTimer(this))
{
ui->setupUi(this);
m_busyLabel = new AzQtComponents::StyledBusyLabel();
m_busyLabel->SetIsBusy(true);
m_busyLabel->SetBusyIconSize(14);
ui->m_header->addWidget(m_busyLabel);
m_reportModel = new AzQtComponents::StyledDetailsTableModel();
m_reportModel->AddColumn("Status", AzQtComponents::StyledDetailsTableModel::StatusIcon);
if (layout == Layout::Exporting)
{
m_reportModel->AddColumn("Platform");
}
m_reportModel->AddColumn("Message");
m_reportModel->AddColumnAlias("message", "Message");
auto messageFilterModel = new Internal::QtWebEngineMessageFilter(this);
messageFilterModel->setSourceModel(m_reportModel);
m_reportView = new AzQtComponents::StyledDetailsTableView();
m_reportView->setModel(messageFilterModel);
ui->m_reportArea->addWidget(m_reportView);
UpdateColumnSizes();
connect(m_overlay, &UI::OverlayWidget::LayerRemoved, this, &ProcessingOverlayWidget::OnLayerRemoved);
BusConnect();
m_resizeTimer->setSingleShot(true);
m_resizeTimer->setInterval(0);
connect(m_resizeTimer, &QTimer::timeout, this, &ProcessingOverlayWidget::UpdateColumnSizes);
}
ProcessingOverlayWidget::~ProcessingOverlayWidget()
{
BusDisconnect();
}
bool ProcessingOverlayWidget::OnPrintf(const char* window, const char* message)
{
if (ShouldProcessMessage())
{
AzQtComponents::StyledDetailsTableModel::TableEntry entry;
if (AzFramework::StringFunc::Find(window, "Success") != AZStd::string::npos)
{
entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusSuccess);
}
else if (AzFramework::StringFunc::Find(window, "Warning") != AZStd::string::npos)
{
entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusWarning);
m_encounteredIssues = true;
}
else if (AzFramework::StringFunc::Find(window, "Error") != AZStd::string::npos)
{
entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusError);
m_encounteredIssues = true;
}
else
{
// To reduce noise in the report widget, only show success, warning and error messages.
return false;
}
entry.Add("Message", message);
CopyTraceContext(entry);
m_reportModel->AddEntry(entry);
}
return false;
}
bool ProcessingOverlayWidget::OnError(const char* /*window*/, const char* message)
{
if (ShouldProcessMessage())
{
AzQtComponents::StyledDetailsTableModel::TableEntry entry;
entry.Add("Message", message);
entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusError);
CopyTraceContext(entry);
m_reportModel->AddEntry(entry);
m_encounteredIssues = true;
return true;
}
return false;
}
bool ProcessingOverlayWidget::OnWarning(const char* /*window*/, const char* message)
{
if (ShouldProcessMessage())
{
AzQtComponents::StyledDetailsTableModel::TableEntry entry;
entry.Add("Message", message);
entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusWarning);
CopyTraceContext(entry);
m_reportModel->AddEntry(entry);
m_encounteredIssues = true;
return true;
}
return false;
}
bool ProcessingOverlayWidget::OnAssert(const char* message)
{
if (ShouldProcessMessage())
{
AzQtComponents::StyledDetailsTableModel::TableEntry entry;
entry.Add("Message", message);
entry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusError);
CopyTraceContext(entry);
m_reportModel->AddEntry(entry);
m_encounteredIssues = true;
// Don't return true here as assert should pop a window.
}
return false;
}
void ProcessingOverlayWidget::OnLayerRemoved(int layerId)
{
if (layerId == m_layerId)
{
delete m_progressLabel;
m_progressLabel = nullptr;
layerId = UI::OverlayWidget::s_invalidOverlayIndex;
emit Closing();
}
}
int ProcessingOverlayWidget::PushToOverlay()
{
AZ_Assert(m_layerId == UI::OverlayWidget::s_invalidOverlayIndex, "Processing overlay widget already pushed.");
if (m_layerId != UI::OverlayWidget::s_invalidOverlayIndex)
{
return m_layerId;
}
UI::OverlayWidgetButtonList buttons;
UI::OverlayWidgetButton button;
button.m_text = "Ok";
button.m_triggersPop = true;
button.m_isCloseButton = true;
button.m_enabledCheck = [this]() -> bool
{
return CanClose();
};
buttons.push_back(&button);
m_progressLabel = new QLabel("Processing...");
m_progressLabel->setAlignment(Qt::AlignCenter);
m_layerId = m_overlay->PushLayer(m_progressLabel, this, "File progress", buttons);
return m_layerId;
}
bool ProcessingOverlayWidget::GetAutoCloseOnSuccess() const
{
return m_autoCloseOnSuccess;
}
void ProcessingOverlayWidget::SetAutoCloseOnSuccess(bool closeOnComplete)
{
m_autoCloseOnSuccess = closeOnComplete;
}
bool ProcessingOverlayWidget::HasProcessingCompleted() const
{
return m_isProcessingComplete;
}
void ProcessingOverlayWidget::SetAndStartProcessingHandler(const AZStd::shared_ptr<ProcessingHandler>& handler)
{
AZ_Assert(handler, "Processing handler was null");
AZ_Assert(!m_targetHandler, "A handler has already been assigned. Only one can be active per layer at any given time.");
if (m_targetHandler)
{
return;
}
m_targetHandler = handler;
connect(m_targetHandler.get(), &ProcessingHandler::StatusMessageUpdated, this, &ProcessingOverlayWidget::OnSetStatusMessage);
connect(m_targetHandler.get(), &ProcessingHandler::AddLogEntry, this, &ProcessingOverlayWidget::AddLogEntry);
connect(m_targetHandler.get(), &ProcessingHandler::ProcessingComplete, this, &ProcessingOverlayWidget::OnProcessingComplete);
handler->BeginProcessing();
}
AZStd::shared_ptr<ProcessingHandler> ProcessingOverlayWidget::GetProcessingHandler() const
{
return m_targetHandler;
}
void ProcessingOverlayWidget::BlockClosing()
{
m_isClosingBlocked = true;
}
void ProcessingOverlayWidget::UnblockClosing()
{
m_isClosingBlocked = false;
SetUIToCompleteState();
}
void ProcessingOverlayWidget::AddLogEntry(const AzToolsFramework::Logging::LogEntry& entry)
{
if (entry.GetSeverity() == AzToolsFramework::Logging::LogEntry::Severity::Message)
{
return;
}
m_encounteredIssues = true;
bool hasStatus = false;
AzQtComponents::StyledDetailsTableModel::TableEntry reportEntry;
for (auto& field : entry.GetFields())
{
size_t offset = 0;
hasStatus = hasStatus || AzFramework::StringFunc::Equal("status", field.second.m_name.c_str());
if (AzFramework::StringFunc::Equal("message", field.second.m_name.c_str()))
{
if (field.second.m_value.length() > 2)
{
// Removing the prefixes such as "W: " and "E: ".
if (field.second.m_value[1] == ':' && field.second.m_value[2] == ' ')
{
offset = 3;
}
}
}
reportEntry.Add(field.second.m_name.c_str(), field.second.m_value.c_str() + offset);
}
if (!hasStatus)
{
if (entry.GetSeverity() == AzToolsFramework::Logging::LogEntry::Severity::Error)
{
reportEntry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusError);
}
else if (entry.GetSeverity() == AzToolsFramework::Logging::LogEntry::Severity::Warning)
{
reportEntry.Add("Status", AzQtComponents::StyledDetailsTableModel::StatusWarning);
}
}
time_t time = QDateTime::fromMSecsSinceEpoch(entry.GetRecordedTime()).toTime_t();
struct tm timeInfo;
#if defined(AZ_PLATFORM_WINDOWS)
localtime_s(&timeInfo, &time);
#else
localtime_r(&time, &timeInfo);
#endif
char buffer[128];
std::strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeInfo);
reportEntry.Add("Time", buffer);
std::strftime(buffer, sizeof(buffer), "%A, %B %d, %Y", &timeInfo);
reportEntry.Add("Date", buffer);
m_reportModel->AddEntry(reportEntry);
m_resizeTimer->start();
}
void ProcessingOverlayWidget::OnProcessingComplete()
{
m_isProcessingComplete = true;
SetUIToCompleteState();
if (!m_encounteredIssues && m_autoCloseOnSuccess)
{
close();
}
else if (m_progressLabel != nullptr)
{
m_progressLabel->setText("Close the processing report to continue editing settings.");
}
}
void ProcessingOverlayWidget::OnSetStatusMessage(const AZStd::string& message)
{
m_busyLabel->SetText(message.c_str());
}
void ProcessingOverlayWidget::SetUIToCompleteState()
{
if (CanClose())
{
if (m_overlay && m_layerId != UI::OverlayWidget::s_invalidOverlayIndex)
{
m_overlay->RefreshLayer(m_layerId);
}
m_busyLabel->SetIsBusy(false);
}
}
bool ProcessingOverlayWidget::CanClose() const
{
return !m_isClosingBlocked && m_isProcessingComplete;
}
bool ProcessingOverlayWidget::ShouldProcessMessage() const
{
AZStd::shared_ptr<const AzToolsFramework::Debug::TraceContextStack> stack = m_traceStackHandler.GetCurrentStack();
if (stack)
{
for (size_t i = 0; i < stack->GetStackCount(); ++i)
{
if (stack->GetType(i) == AzToolsFramework::Debug::TraceContextStackInterface::ContentType::UuidType)
{
if (stack->GetUuidValue(i) == m_traceTag)
{
return true;
}
}
}
}
return false;
}
void ProcessingOverlayWidget::CopyTraceContext(AzQtComponents::StyledDetailsTableModel::TableEntry& entry) const
{
AZStd::shared_ptr<const AzToolsFramework::Debug::TraceContextStack> stack = m_traceStackHandler.GetCurrentStack();
if (stack)
{
AZStd::string value;
for (size_t i = 0; i < stack->GetStackCount(); ++i)
{
if (stack->GetType(i) != AzToolsFramework::Debug::TraceContextStackInterface::ContentType::UuidType)
{
const char* key = stack->GetKey(i);
AzToolsFramework::Debug::TraceContextLogFormatter::PrintValue(value, *stack, i);
entry.Add(key, value.c_str());
value.clear();
}
}
}
}
void ProcessingOverlayWidget::UpdateColumnSizes()
{
const int headerPadding = 5;
m_reportView->resizeColumnsToContents();
m_reportView->horizontalHeader()->resizeSection(0,
fontMetrics().horizontalAdvance("Status")
+ style()->pixelMetric(QStyle::PM_HeaderMarkSize) + headerPadding);
}
} // SceneUI
} // SceneAPI
} // AZ
#include <CommonWidgets/moc_ProcessingOverlayWidget.cpp>
@@ -0,0 +1,166 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzFramework/Asset/AssetSystemBus.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzToolsFramework/Debug/TraceContextMultiStackHandler.h>
#include <AzQtComponents/Components/StyledDetailsTableModel.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#include <QScopedPointer>
#include <QWidget>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
#include <QSortFilterProxyModel>
#endif
class QCloseEvent;
class QLabel;
class QTimer;
namespace AzQtComponents
{
class StyledBusyLabel;
class StyledDetailsTableView;
}
namespace AzToolsFramework
{
namespace Logging
{
class LogEntry;
}
}
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
// The qt-generated ui code (from the .ui)
namespace Ui
{
class ProcessingOverlayWidget;
}
namespace Internal
{
// This QSortFilterProxyModel filters out an erroneous message.
// ResourceCompiler loads all gems. There are some gems which depend on
// EditorLib, which loads QtWebEngineWidgets. QtWebEngineWidgets prints a
// warning if it is loaded after a QCoreApplication has been instantiated,
// but no QOpenGLContext exists, which is always the case with
// ResourceCompiler.
// The correct fix would be to remove all dependencies on EditorLib from
// gems.
class QtWebEngineMessageFilter
: public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit QtWebEngineMessageFilter(QObject* parent = nullptr);
~QtWebEngineMessageFilter() override;
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
};
}
class ProcessingHandler;
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
class SCENE_UI_API ProcessingOverlayWidget
: public QWidget
, public Debug::TraceMessageBus::Handler
{
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(ProcessingOverlayWidget, SystemAllocator, 0);
//! Layout configurations for the various stages the Scene Settings can be in.
enum class Layout
{
Loading,
Resetting,
Exporting
};
ProcessingOverlayWidget(UI::OverlayWidget* overlay, Layout layout, Uuid traceTag);
~ProcessingOverlayWidget() override;
bool OnPrintf(const char* window, const char* message) override;
bool OnError(const char* window, const char* message) override;
bool OnWarning(const char* window, const char* message) override;
bool OnAssert(const char* message) override;
int PushToOverlay();
void SetAndStartProcessingHandler(const AZStd::shared_ptr<ProcessingHandler>& handler);
AZStd::shared_ptr<ProcessingHandler> GetProcessingHandler() const;
bool GetAutoCloseOnSuccess() const;
void SetAutoCloseOnSuccess(bool autoCloseOnSuccess);
bool HasProcessingCompleted() const;
void BlockClosing();
void UnblockClosing();
signals:
void Closing();
public slots:
void AddLogEntry(const AzToolsFramework::Logging::LogEntry& entry);
void OnLayerRemoved(int layerId);
void OnSetStatusMessage(const AZStd::string& message);
void OnProcessingComplete();
void UpdateColumnSizes();
private:
void SetUIToCompleteState();
bool CanClose() const;
bool ShouldProcessMessage() const;
void CopyTraceContext(AzQtComponents::StyledDetailsTableModel::TableEntry& entry) const;
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AzToolsFramework::Debug::TraceContextMultiStackHandler m_traceStackHandler;
Uuid m_traceTag;
QScopedPointer<Ui::ProcessingOverlayWidget> ui;
AZStd::shared_ptr<ProcessingHandler> m_targetHandler;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
UI::OverlayWidget* m_overlay;
AzQtComponents::StyledBusyLabel* m_busyLabel;
AzQtComponents::StyledDetailsTableView* m_reportView;
AzQtComponents::StyledDetailsTableModel* m_reportModel;
QLabel* m_progressLabel;
int m_layerId;
QTimer* m_resizeTimer;
bool m_isProcessingComplete;
bool m_isClosingBlocked;
bool m_autoCloseOnSuccess;
bool m_encounteredIssues;
};
}
}
}
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::SceneUI::ProcessingOverlayWidget</class>
<widget class="QWidget" name="AZ::SceneAPI::SceneUI::ProcessingOverlayWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>133</width>
<height>123</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<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>
<layout class="QVBoxLayout" name="m_header"/>
</item>
<item>
<layout class="QVBoxLayout" name="m_reportArea"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+70
View File
@@ -0,0 +1,70 @@
/*
* 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 <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Module/Environment.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <SceneAPI/SceneUI/GraphMetaInfoHandler.h>
#include <SceneAPI/SceneUI/ManifestMetaInfoHandler.h>
#include <SceneAPI/SceneUI/RowWidgets/HeaderHandler.h>
#include <SceneAPI/SceneUI/RowWidgets/NodeListSelectionHandler.h>
#include <SceneAPI/SceneUI/RowWidgets/NodeTreeSelectionHandler.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestVectorHandler.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestNameHandler.h>
#include <SceneAPI/SceneUI/RowWidgets/TransformRowHandler.h>
static AZ::SceneAPI::UI::GraphMetaInfoHandler* g_graphMetaInfoHandler = nullptr;
static AZ::SceneAPI::UI::ManifestMetaInfoHandler* g_manifestMetaInfoHandler = nullptr;
extern "C" AZ_DLL_EXPORT void InitializeDynamicModule(void* env)
{
AZ::Environment::Attach(static_cast<AZ::EnvironmentInstance>(env));
AZ::SceneAPI::UI::HeaderHandler::Register();
AZ::SceneAPI::UI::NodeListSelectionHandler::Register();
AZ::SceneAPI::UI::NodeTreeSelectionHandler::Register();
AZ::SceneAPI::UI::ManifestVectorHandler::Register();
AZ::SceneAPI::SceneUI::ManifestNameHandler::Register();
AZ::SceneAPI::SceneUI::TranformRowHandler::Register();
g_graphMetaInfoHandler = aznew AZ::SceneAPI::UI::GraphMetaInfoHandler();
g_manifestMetaInfoHandler = aznew AZ::SceneAPI::UI::ManifestMetaInfoHandler();
}
extern "C" AZ_DLL_EXPORT void Reflect(AZ::SerializeContext*)
{
// provide this empty function, otherwise Reflect from SceneCore is used as fall back on macOS
}
extern "C" AZ_DLL_EXPORT void ReflectBehavior(AZ::BehaviorContext*)
{
// provide this empty function, otherwise Reflect from SceneCore is used as fall back on macOS
}
extern "C" AZ_DLL_EXPORT void UninitializeDynamicModule()
{
delete g_manifestMetaInfoHandler;
g_manifestMetaInfoHandler = nullptr;
delete g_graphMetaInfoHandler;
g_graphMetaInfoHandler = nullptr;
AZ::SceneAPI::SceneUI::TranformRowHandler::Unregister();
AZ::SceneAPI::SceneUI::ManifestNameHandler::Unregister();
AZ::SceneAPI::UI::ManifestVectorHandler::Unregister();
AZ::SceneAPI::UI::NodeTreeSelectionHandler::Unregister();
AZ::SceneAPI::UI::NodeListSelectionHandler::Unregister();
AZ::SceneAPI::UI::HeaderHandler::Unregister();
AZ::Environment::Detach();
}
@@ -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.
*
*/
#include <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneUI/GraphMetaInfoHandler.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IBoneData.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/ITransform.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(GraphMetaInfoHandler, SystemAllocator, 0)
GraphMetaInfoHandler::GraphMetaInfoHandler()
{
BusConnect();
}
GraphMetaInfoHandler::~GraphMetaInfoHandler()
{
BusDisconnect();
}
void GraphMetaInfoHandler::GetIconPath(AZStd::string& iconPath, const DataTypes::IGraphObject* target)
{
if (target->RTTI_IsTypeOf(DataTypes::IMeshData::TYPEINFO_Uuid()))
{
iconPath = ":/SceneUI/Graph/MeshIcon.png";
}
else if (target->RTTI_IsTypeOf(DataTypes::IBoneData::TYPEINFO_Uuid()))
{
iconPath = ":/SceneUI/Graph/BoneIcon.png";
}
}
void GraphMetaInfoHandler::GetToolTip(AZStd::string& toolTip, const DataTypes::IGraphObject* target)
{
if (target->RTTI_IsTypeOf(DataTypes::ITransform::TYPEINFO_Uuid()))
{
toolTip = "Transform information changes the translation, rotation and/or scale. Multiple transform will be added together.";
}
else if (target->RTTI_IsTypeOf(DataTypes::IMeshData::TYPEINFO_Uuid()))
{
toolTip = "MeshData contains the vertex information to create the mesh for the 3D model.";
}
else if (target->RTTI_IsTypeOf(DataTypes::IBoneData::TYPEINFO_Uuid()))
{
toolTip = "Bones make up an animation skeleton. Usually bones are hierarchically chained together and the root bone will be available for selection.";
}
}
} // SceneData
} // SceneAPI
} // AZ
@@ -0,0 +1,37 @@
#pragma once
/*
* 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 <AzCore/Memory/Memory.h>
#include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
class GraphMetaInfoHandler : public Events::GraphMetaInfoBus::Handler
{
public:
AZ_CLASS_ALLOCATOR_DECL
GraphMetaInfoHandler();
~GraphMetaInfoHandler() override;
void GetIconPath(AZStd::string& iconPath, const DataTypes::IGraphObject* target) override;
void GetToolTip(AZStd::string& toolTip, const DataTypes::IGraphObject* target) override;
};
} // SceneData
} // SceneAPI
} // AZ
@@ -0,0 +1,62 @@
/*
* 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 <AzCore/Component/TickBus.h>
#include <AzCore/std/parallel/thread.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneUI/Handlers/ProcessingHandlers/AsyncOperationProcessingHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
AsyncOperationProcessingHandler::AsyncOperationProcessingHandler(Uuid traceTag, AZStd::function<void()> targetFunction, AZStd::function<void()> onComplete, QObject* parent)
: ProcessingHandler(traceTag, parent)
, m_operationToRun(targetFunction)
, m_onComplete(onComplete)
{
}
void AsyncOperationProcessingHandler::BeginProcessing()
{
emit StatusMessageUpdated("Waiting for background processes to complete...");
m_thread.reset(
new AZStd::thread(
[this]()
{
AZ_TraceContext("Tag", m_traceTag);
m_operationToRun();
EBUS_QUEUE_FUNCTION(AZ::TickBus, AZStd::bind(&AsyncOperationProcessingHandler::OnBackgroundOperationComplete, this));
}
)
);
}
void AsyncOperationProcessingHandler::OnBackgroundOperationComplete()
{
m_thread->detach();
m_thread.reset(nullptr);
emit StatusMessageUpdated("Processing complete");
if (m_onComplete)
{
m_onComplete();
}
emit ProcessingComplete();
}
}
}
}
#include <Handlers/ProcessingHandlers/moc_AsyncOperationProcessingHandler.cpp>
@@ -0,0 +1,53 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <SceneAPI/SceneUI/Handlers/ProcessingHandlers/ProcessingHandler.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
namespace AZStd
{
class thread;
}
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
class SCENE_UI_API AsyncOperationProcessingHandler : public ProcessingHandler
{
Q_OBJECT
public:
AsyncOperationProcessingHandler(Uuid traceTag, AZStd::function<void()> targetFunction, AZStd::function<void()> onComplete = nullptr, QObject* parent = nullptr);
~AsyncOperationProcessingHandler() override = default;
void BeginProcessing() override;
private:
void OnBackgroundOperationComplete();
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZStd::function<void()> m_operationToRun;
AZStd::function<void()> m_onComplete;
AZStd::unique_ptr<AZStd::thread> m_thread;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
}
}
}
@@ -0,0 +1,87 @@
/*
* 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 <QRegExp>
#include <AzCore/Casting/numeric_cast.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <AzToolsFramework/UI/Logging/LogEntry.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneUI/Handlers/ProcessingHandlers/ExportJobProcessingHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
ExportJobProcessingHandler::ExportJobProcessingHandler(Uuid traceTag, const AZStd::string& sourceAssetPath, QObject* parent)
: ProcessingHandler(traceTag, parent)
, m_sourceAssetPath(sourceAssetPath)
{
}
void ExportJobProcessingHandler::BeginProcessing()
{
m_jobWatcher.reset(new JobWatcher(m_sourceAssetPath, m_traceTag));
connect(m_jobWatcher.get(), &JobWatcher::JobProcessingComplete, this, &ExportJobProcessingHandler::OnJobProcessingComplete);
connect(m_jobWatcher.get(), &JobWatcher::AllJobsComplete, this, &ExportJobProcessingHandler::OnAllJobsComplete);
m_jobWatcher->StartMonitoring();
emit StatusMessageUpdated("File processing...");
}
void ExportJobProcessingHandler::OnJobQueryFailed([[maybe_unused]] const char* message)
{
emit StatusMessageUpdated("Processing failed.");
AZ_TracePrintf(Utilities::ErrorWindow, "%s", message);
emit ProcessingComplete();
}
void ExportJobProcessingHandler::OnJobProcessingComplete(const AZStd::string& platform, [[maybe_unused]] AZ::u64 jobId, bool success, const AZStd::string& fullLogText)
{
AZ_TraceContext("Platform", platform);
if (!fullLogText.empty())
{
bool parseResult = AzToolsFramework::Logging::LogEntry::ParseLog(fullLogText.c_str(), aznumeric_cast<AZ::u64>(fullLogText.length()),
[this](const AzToolsFramework::Logging::LogEntry& entry)
{
emit AddLogEntry(entry);
});
if (!parseResult)
{
AZ_TracePrintf(Utilities::ErrorWindow, "Failed to parse log. See Asset Processor for more info.");
}
}
if (success)
{
AZ_TracePrintf(Utilities::SuccessWindow, "Job #%i compiled successfully", jobId);
}
else
{
AZ_TracePrintf(Utilities::ErrorWindow, "Job #%i failed", jobId);
}
}
void ExportJobProcessingHandler::OnAllJobsComplete()
{
emit StatusMessageUpdated("All jobs completed.");
emit ProcessingComplete();
}
}
}
}
#include <Handlers/ProcessingHandlers/moc_ExportJobProcessingHandler.cpp>
@@ -0,0 +1,52 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <SceneAPI/SceneUI/Handlers/ProcessingHandlers/ProcessingHandler.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/containers/list.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#include <SceneAPI/SceneUI/CommonWidgets/JobWatcher.h>
#endif
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
class SCENE_UI_API ExportJobProcessingHandler : public ProcessingHandler
{
Q_OBJECT
public:
ExportJobProcessingHandler(Uuid traceTag, const AZStd::string& sourceAssetPath, QObject* parent = nullptr);
~ExportJobProcessingHandler() override = default;
void BeginProcessing() override;
private slots:
void OnJobQueryFailed(const char* message);
void OnJobProcessingComplete(const AZStd::string& platform, AZ::u64 jobId, bool success, const AZStd::string& fullLogText);
void OnAllJobsComplete();
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZStd::string m_sourceAssetPath;
AZStd::unique_ptr<JobWatcher> m_jobWatcher;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,30 @@
/*
* 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 <SceneAPI/SceneUI/Handlers/ProcessingHandlers/ProcessingHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
ProcessingHandler::ProcessingHandler(Uuid traceTag, QObject* parent)
: QObject(parent)
, m_traceTag(traceTag)
{
}
}
}
}
#include <Handlers/ProcessingHandlers/moc_ProcessingHandler.cpp>
@@ -0,0 +1,59 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QObject>
#include <AzCore/std/string/string.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
namespace AzToolsFramework
{
namespace Logging
{
class LogEntry;
}
}
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
class SCENE_UI_API ProcessingHandler : public QObject
{
Q_OBJECT
public:
// The traceTag uuid is added to the trace context stack before work is done. This allows
// for filtering messages that are send by this processing handler.
explicit ProcessingHandler(Uuid traceTag, QObject* parent = nullptr);
~ProcessingHandler() override = default;
virtual void BeginProcessing() = 0;
signals:
void AddLogEntry(const AzToolsFramework::Logging::LogEntry& entry);
void StatusMessageUpdated(const AZStd::string& message);
void ProcessingComplete();
protected:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
Uuid m_traceTag;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
}
}
}
@@ -0,0 +1,59 @@
/*
* 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 <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneUI/ManifestMetaInfoHandler.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkeletonGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/ISkinGroup.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IAnimationGroup.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(ManifestMetaInfoHandler, SystemAllocator, 0)
ManifestMetaInfoHandler::ManifestMetaInfoHandler()
{
BusConnect();
}
ManifestMetaInfoHandler::~ManifestMetaInfoHandler()
{
BusDisconnect();
}
void ManifestMetaInfoHandler::GetIconPath(AZStd::string& iconPath, const DataTypes::IManifestObject& target)
{
if (target.RTTI_IsTypeOf(DataTypes::IMeshGroup::TYPEINFO_Uuid()))
{
iconPath = ":/SceneUI/Manifest/MeshGroupIcon.png";
}
else if (target.RTTI_IsTypeOf(DataTypes::ISkeletonGroup::TYPEINFO_Uuid()))
{
iconPath = ":/SceneUI/Manifest/SkeletonGroupIcon.png";
}
else if (target.RTTI_IsTypeOf(DataTypes::ISkinGroup::TYPEINFO_Uuid()))
{
iconPath = ":/SceneUI/Manifest/SkinGroupIcon.png";
}
else if (target.RTTI_IsTypeOf(DataTypes::IAnimationGroup::TYPEINFO_Uuid()))
{
iconPath = ":/SceneUI/Manifest/AnimationGroupIcon.png";
}
}
} // SceneData
} // SceneAPI
} // AZ
@@ -0,0 +1,36 @@
#pragma once
/*
* 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 <AzCore/Memory/Memory.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
class ManifestMetaInfoHandler : public Events::ManifestMetaInfoBus::Handler
{
public:
AZ_CLASS_ALLOCATOR_DECL
ManifestMetaInfoHandler();
~ManifestMetaInfoHandler() override;
void GetIconPath(AZStd::string& iconPath, const DataTypes::IManifestObject& target) override;
};
} // SceneData
} // SceneAPI
} // AZ
@@ -0,0 +1,88 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneUI/RowWidgets/HeaderHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(HeaderHandler, SystemAllocator, 0)
HeaderHandler* HeaderHandler::s_instance = nullptr;
QWidget* HeaderHandler::CreateGUI(QWidget* parent)
{
return aznew HeaderWidget(parent);
}
u32 HeaderHandler::GetHandlerName() const
{
return AZ_CRC("Header", 0x6e72a8c1);
}
bool HeaderHandler::AutoDelete() const
{
return false;
}
bool HeaderHandler::IsDefaultHandler() const
{
return true;
}
void HeaderHandler::ConsumeAttribute(HeaderWidget* /*widget*/, u32 /*attrib*/,
AzToolsFramework::PropertyAttributeReader* /*attrValue*/, const char* /*debugName*/)
{
// No attributes are used by this handler, but the function is mandatory.
}
void HeaderHandler::WriteGUIValuesIntoProperty(size_t /*index*/, HeaderWidget* GUI,
property_t& instance, AzToolsFramework::InstanceDataNode* /*node*/)
{
instance = *GUI->GetManifestObject();
}
bool HeaderHandler::ReadValuesIntoGUI(size_t /*index*/, HeaderWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* /*node*/)
{
GUI->SetManifestObject(&instance);
return false;
}
void HeaderHandler::Register()
{
if (!s_instance)
{
s_instance = aznew HeaderHandler();
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, s_instance);
}
}
void HeaderHandler::Unregister()
{
if (s_instance)
{
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, UnregisterPropertyType, s_instance);
delete s_instance;
s_instance = nullptr;
}
}
} // UI
} // SceneAPI
} // AZ
#include <RowWidgets/moc_HeaderHandler.cpp>
@@ -0,0 +1,69 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <AzCore/Math/Crc.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <SceneAPI/SceneUI/RowWidgets/HeaderWidget.h>
#include <SceneAPI/SceneData/Groups/MeshGroup.h>
#endif
class QWidget;
/*
=============================================================
= Handler Documentation =
=============================================================
Handler Name: "Header"
*/
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
class HeaderHandler
: public QObject, public AzToolsFramework::PropertyHandler<DataTypes::IManifestObject, HeaderWidget>
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
QWidget* CreateGUI(QWidget* parent) override;
u32 GetHandlerName() const override;
bool AutoDelete() const override;
bool IsDefaultHandler() const override;
void ConsumeAttribute(HeaderWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, HeaderWidget* GUI, property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, HeaderWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
static void Register();
static void Unregister();
private:
static HeaderHandler* s_instance;
};
} // UI
} // SceneAPI
} // AZ
@@ -0,0 +1,205 @@
/*
* 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 <QEvent>
#include <RowWidgets/ui_HeaderWidget.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#include <SceneAPI/SceneUI/RowWidgets/HeaderWidget.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestVectorWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
static void InitSceneUIHeaderWidgetResources()
{
Q_INIT_RESOURCE(Icons);
}
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(HeaderWidget, SystemAllocator, 0)
HeaderWidget::HeaderWidget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::HeaderWidget())
, m_target(nullptr)
, m_nameIsEditable(false)
, m_sceneManifest(nullptr)
{
InitSceneUIHeaderWidgetResources();
ui->setupUi(this);
ui->m_icon->hide();
ui->m_deleteButton->setIcon(QIcon(":/PropertyEditor/Resources/cross-small.png"));
connect(ui->m_deleteButton, &QToolButton::clicked, this, &HeaderWidget::DeleteObject);
ui->m_deleteButton->hide();
ManifestWidget* root = ManifestWidget::FindRoot(this);
AZ_Assert(root, "HeaderWidget is not a child of the ManifestWidget");
if (root)
{
m_sceneManifest = &root->GetScene()->GetManifest();
}
}
void HeaderWidget::SetManifestObject(const DataTypes::IManifestObject* target)
{
AZ_TraceContext("New target", GetSerializedName(target));
m_target = target;
ui->m_nameLabel->setText(GetSerializedName(target));
UpdateDeletable();
SetIcon(target);
}
const DataTypes::IManifestObject* HeaderWidget::GetManifestObject() const
{
return m_target;
}
void HeaderWidget::DeleteObject()
{
AZ_TraceContext("Delete target", GetSerializedName(m_target));
if (m_sceneManifest)
{
Containers::SceneManifest::Index index = m_sceneManifest->FindIndex(m_target);
if (index != Containers::SceneManifest::s_invalidIndex)
{
AZ_TraceContext("Manifest index", static_cast<int>(index));
ManifestWidget* root = ManifestWidget::FindRoot(this);
// The manifest object could be a root element at the manifest page level so it needs to be
// removed from there as well in that case.
if (root->RemoveObject(m_sceneManifest->GetValue(index)) && m_sceneManifest->RemoveEntry(m_target))
{
m_target = nullptr;
// Hide and disable the button so when users spam the delete button only a single click is recorded.
ui->m_deleteButton->hide();
ui->m_deleteButton->setEnabled(false);
return;
}
else
{
AZ_TracePrintf(Utilities::LogWindow, "Unable to delete manifest object from manifest.");
}
}
}
QObject* widget = this->parent();
while (widget != nullptr)
{
ManifestVectorWidget* manifestVectorWidget = qobject_cast<ManifestVectorWidget*>(widget);
if (manifestVectorWidget)
{
if (manifestVectorWidget->RemoveManifestObject(m_target))
{
m_target = nullptr;
// Hide and disable the button so when users spam the delete button only a single click is recorded.
ui->m_deleteButton->hide();
ui->m_deleteButton->setEnabled(false);
}
else
{
AZ_TracePrintf(Utilities::WarningWindow, "Parent collection did not contain this ManifestObject");
}
return;
}
widget = widget->parent();
}
AZ_TracePrintf(Utilities::ErrorWindow, "No parent valid parent collection found.");
}
void HeaderWidget::UpdateDeletable()
{
ui->m_deleteButton->hide();
if (m_sceneManifest)
{
Containers::SceneManifest::Index index = m_sceneManifest->FindIndex(m_target);
if (index != Containers::SceneManifest::s_invalidIndex)
{
ui->m_deleteButton->show();
return;
}
}
QObject* widget = this->parent();
while(widget != nullptr)
{
ManifestVectorWidget* manifestVectorWidget = qobject_cast<ManifestVectorWidget*>(widget);
if (manifestVectorWidget && manifestVectorWidget->ContainsManifestObject(m_target))
{
ui->m_deleteButton->show();
break;
}
widget = widget->parent();
}
}
const char* HeaderWidget::GetSerializedName(const DataTypes::IManifestObject* target) const
{
SerializeContext* context = nullptr;
EBUS_EVENT_RESULT(context, ComponentApplicationBus, GetSerializeContext);
if (context)
{
const SerializeContext::ClassData* classData = context->FindClassData(target->RTTI_GetType());
if (classData)
{
if (classData->m_editData)
{
return classData->m_editData->m_name;
}
return classData->m_name;
}
}
return "<type not registered>";
}
void HeaderWidget::SetIcon(const DataTypes::IManifestObject* target)
{
if (!target)
{
return;
}
AZStd::string iconPath;
EBUS_EVENT(Events::ManifestMetaInfoBus, GetIconPath, iconPath, *target);
if (iconPath.empty())
{
ui->m_icon->hide();
}
else
{
ui->m_icon->setPixmap(QPixmap(iconPath.c_str()));
ui->m_icon->show();
}
}
} // UI
} // SceneAPI
} // AZ
#include <RowWidgets/moc_HeaderWidget.cpp>
@@ -0,0 +1,74 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/std/string/string.h>
#include <AzCore/Memory/SystemAllocator.h>
#endif
namespace AZ
{
namespace SceneAPI
{
namespace Containers
{
class SceneManifest;
}
namespace DataTypes
{
class IManifestObject;
}
namespace UI
{
// QT space
namespace Ui
{
class HeaderWidget;
}
class HeaderWidget : public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
enum NameStack
{
Label,
EditField
};
explicit HeaderWidget(QWidget* parent);
void SetManifestObject(const DataTypes::IManifestObject* target);
const DataTypes::IManifestObject* GetManifestObject() const;
protected:
bool InitSceneManifest();
virtual void DeleteObject();
virtual void UpdateDeletable();
virtual const char* GetSerializedName(const DataTypes::IManifestObject* target) const;
virtual void SetIcon(const DataTypes::IManifestObject* target);
AZStd::string m_objectName;
QScopedPointer<Ui::HeaderWidget> ui;
Containers::SceneManifest* m_sceneManifest; // Reference only, does not point to a local instance.
const DataTypes::IManifestObject* m_target; // Reference only, does not point to a local instance.
bool m_nameIsEditable;
};
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::UI::HeaderWidget</class>
<widget class="QWidget" name="AZ::SceneAPI::UI::HeaderWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>150</width>
<height>20</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>20</verstretch>
</sizepolicy>
</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="m_icon">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>16</horstretch>
<verstretch>16</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_nameLabel">
<property name="text">
<string>Name label</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_deleteButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>16</horstretch>
<verstretch>16</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="toolTip">
<string>Delete this entry</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,108 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestNameHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
AZ_CLASS_ALLOCATOR_IMPL(ManifestNameHandler, SystemAllocator, 0)
ManifestNameHandler* ManifestNameHandler::s_instance = nullptr;
QWidget* ManifestNameHandler::CreateGUI(QWidget* parent)
{
ManifestNameWidget* instance = aznew ManifestNameWidget(parent);
connect(instance, &ManifestNameWidget::valueChanged, this,
[instance]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, instance);
});
return instance;
}
u32 ManifestNameHandler::GetHandlerName() const
{
return AZ_CRC("ManifestName", 0x5215b349);
}
bool ManifestNameHandler::AutoDelete() const
{
return false;
}
void ManifestNameHandler::ConsumeAttribute(ManifestNameWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
AZ_TraceContext("Attribute name", debugName);
if (attrib == AZ_CRC("FilterType", 0x2661cf01))
{
ConsumeFilterTypeAttribute(widget, attrValue);
}
}
void ManifestNameHandler::WriteGUIValuesIntoProperty(size_t /*index*/, ManifestNameWidget* GUI,
property_t& instance, AzToolsFramework::InstanceDataNode* /*node*/)
{
instance = static_cast<property_t>(GUI->GetName());
}
bool ManifestNameHandler::ReadValuesIntoGUI(size_t /*index*/, ManifestNameWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* /*node*/)
{
GUI->SetName(instance);
return false;
}
void ManifestNameHandler::Register()
{
if (!s_instance)
{
s_instance = aznew ManifestNameHandler();
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, s_instance);
}
}
void ManifestNameHandler::Unregister()
{
if (s_instance)
{
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, UnregisterPropertyType, s_instance);
delete s_instance;
s_instance = nullptr;
}
}
void ManifestNameHandler::ConsumeFilterTypeAttribute(ManifestNameWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue)
{
Uuid filterType;
if (attrValue->Read<Uuid>(filterType))
{
widget->SetFilterType(filterType);
}
else
{
AZ_Assert(false, "Failed to read uuid from 'FilterType' attribute.");
}
}
} // SceneUI
} // SceneAPI
} // AZ
#include <RowWidgets/moc_ManifestNameHandler.cpp>
@@ -0,0 +1,62 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestNameWidget.h>
#endif
class QWidget;
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
//Available Attributes:
// - "FilterType" - Uuid for the type(s) to filter for. If set, the name will only be unique for
// classes of this type or derived classes.
class ManifestNameHandler
: public QObject, public AzToolsFramework::PropertyHandler<AZStd::string, ManifestNameWidget>
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
QWidget* CreateGUI(QWidget* parent) override;
u32 GetHandlerName() const override;
bool AutoDelete() const;
void ConsumeAttribute(ManifestNameWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, ManifestNameWidget* GUI, property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, ManifestNameWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
static void Register();
static void Unregister();
protected:
virtual void ConsumeFilterTypeAttribute(ManifestNameWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
private:
static ManifestNameHandler* s_instance;
};
} // SceneUI
} // SceneAPI
} // AZ
@@ -0,0 +1,118 @@
/*
* 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 <QStyle>
#include <AzFramework/StringFunc/StringFunc.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
#include <SceneAPI/SceneCore/DataTypes/IManifestObject.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestNameWidget.h>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
AZ_CLASS_ALLOCATOR_IMPL(ManifestNameWidget, SystemAllocator, 0)
ManifestNameWidget::ManifestNameWidget(QWidget* parent)
: QLineEdit(parent)
, m_filterType(DataTypes::IManifestObject::TYPEINFO_Uuid())
, m_inFailureState(false)
{
connect(this, &QLineEdit::textChanged, this, &ManifestNameWidget::OnTextChanged);
}
void ManifestNameWidget::SetName(const char* name)
{
setText(name);
UpdateStatus(name, false);
}
void ManifestNameWidget::SetName(const AZStd::string& name)
{
setText(name.c_str());
UpdateStatus(name, false);
}
const AZStd::string& ManifestNameWidget::GetName() const
{
return m_name;
}
void ManifestNameWidget::SetFilterType(const Uuid& type)
{
m_filterType = type;
}
void ManifestNameWidget::OnTextChanged(const QString& textValue)
{
m_name = textValue.toStdString().c_str();
UpdateStatus(m_name, true);
emit valueChanged(m_name);
}
void ManifestNameWidget::UpdateStatus(const AZStd::string& newName, bool checkAvailability)
{
AZStd::string error;
bool isValid = AzFramework::StringFunc::Path::IsValid(newName.c_str(), false, false, &error);
if (isValid && checkAvailability)
{
isValid = IsAvailableName(error, newName);
}
if (!isValid && !m_inFailureState)
{
m_originalToolTip = toolTip();
setToolTip(error.c_str());
setProperty("inputValid", "false");
m_inFailureState = true;
style()->unpolish(this);
style()->polish(this);
}
else if (isValid && m_inFailureState)
{
setToolTip(m_originalToolTip);
setProperty("inputValid", "true");
m_inFailureState = false;
style()->unpolish(this);
style()->polish(this);
}
}
bool ManifestNameWidget::IsAvailableName(AZStd::string& error, const AZStd::string& name) const
{
const UI::ManifestWidget* manifestWidget = UI::ManifestWidget::FindRoot(this);
if (!manifestWidget)
{
error = "ManifestNameWidget is not a child of a ManifestWidget. For correct name checking this is required.";
return false;
}
const SceneAPI::Containers::SceneManifest& manifest = manifestWidget->GetScene()->GetManifest();
if (!DataTypes::Utilities::IsNameAvailable(name, manifest, m_filterType))
{
error = "Name is already in use.";
return false;
}
return true;
}
} // SceneUI
} // SceneAPI
} // AZ
#include <RowWidgets/moc_ManifestNameWidget.cpp>
@@ -0,0 +1,59 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QString>
#include <QLineEdit>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Memory/SystemAllocator.h>
#endif
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
class ManifestNameWidget : public QLineEdit
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
explicit ManifestNameWidget(QWidget* parent);
void SetName(const char* name);
void SetName(const AZStd::string& name);
const AZStd::string& GetName() const;
void SetFilterType(const Uuid& type);
signals:
void valueChanged(const AZStd::string& newValue);
protected:
void OnTextChanged(const QString& textValue);
void UpdateStatus(const AZStd::string& newName, bool checkAvailability);
bool IsAvailableName(AZStd::string& error, const AZStd::string& name) const;
QString m_originalToolTip;
Uuid m_filterType;
AZStd::string m_name;
bool m_inFailureState;
};
} // SceneUI
} // SceneAPI
} // AZ
@@ -0,0 +1,185 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/DataTypes/Groups/IGroup.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestVectorHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL_INTERNAL(IManifestVectorHandler<ManifestType>, SystemAllocator, 0, template<typename ManifestType>)
template<typename ManifestType> SerializeContext* IManifestVectorHandler<ManifestType>::s_serializeContext = nullptr;
template<typename ManifestType> IManifestVectorHandler<ManifestType>* IManifestVectorHandler<ManifestType>::s_instance = nullptr;
template<typename ManifestType>
QWidget* IManifestVectorHandler<ManifestType>::CreateGUI(QWidget* parent)
{
if(IManifestVectorHandler::s_serializeContext)
{
ManifestVectorWidget* instance = aznew ManifestVectorWidget(IManifestVectorHandler::s_serializeContext, parent);
connect(instance, &ManifestVectorWidget::valueChanged, this,
[instance]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, instance);
});
return instance;
}
else
{
return nullptr;
}
}
template<typename ManifestType>
u32 IManifestVectorHandler<ManifestType>::GetHandlerName() const
{
return AZ_CRC("ManifestVector", 0x895aa9aa);
}
template<typename ManifestType>
bool IManifestVectorHandler<ManifestType>::AutoDelete() const
{
return false;
}
template<typename ManifestType>
void IManifestVectorHandler<ManifestType>::ConsumeAttribute(ManifestVectorWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
AZ_TraceContext("Attribute name", debugName);
if (attrib == AZ_CRC("ObjectTypeName", 0x6559e0c0))
{
AZStd::string name;
if (attrValue->Read<AZStd::string>(name))
{
widget->SetCollectionTypeName(name);
}
}
else if (attrib == AZ_CRC("CollectionName", 0xbbc1c898))
{
AZStd::string name;
if (attrValue->Read<AZStd::string>(name))
{
widget->SetCollectionName(name);
}
}
// Sets the number of entries the user can add through this widget. It doesn't limit
// the amount of entries that can be stored.
else if (attrib == AZ_CRC("Cap", 0x993387b1))
{
size_t cap;
if (attrValue->Read<size_t>(cap))
{
widget->SetCapSize(cap);
}
}
}
template<typename ManifestType>
void IManifestVectorHandler<ManifestType>::WriteGUIValuesIntoProperty(size_t /*index*/, ManifestVectorWidget* GUI,
typename IManifestVectorHandler::property_t& instance, AzToolsFramework::InstanceDataNode* /*node*/)
{
instance.clear();
AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject> > manifestVector = GUI->GetManifestVector();
for (auto& manifestObject : manifestVector)
{
instance.push_back(AZStd::static_pointer_cast<ManifestType>(manifestObject));
}
}
template<typename ManifestType>
bool IManifestVectorHandler<ManifestType>::ReadValuesIntoGUI(size_t /*index*/, ManifestVectorWidget* GUI, const typename IManifestVectorHandler::property_t& instance,
AzToolsFramework::InstanceDataNode* node)
{
AzToolsFramework::InstanceDataNode* parentNode = node->GetParent();
if (parentNode && parentNode->GetClassMetadata() && parentNode->GetClassMetadata()->m_azRtti)
{
AZ_TraceContext("Parent UUID", parentNode->GetClassMetadata()->m_azRtti->GetTypeId());
if (parentNode->GetClassMetadata()->m_azRtti->IsTypeOf(DataTypes::IManifestObject::RTTI_Type()))
{
DataTypes::IManifestObject* owner = static_cast<DataTypes::IManifestObject*>(parentNode->FirstInstance());
GUI->SetManifestVector(instance.begin(), instance.end(), owner);
}
else if (parentNode->GetClassMetadata()->m_azRtti->IsTypeOf(Containers::RuleContainer::RTTI_Type()))
{
AzToolsFramework::InstanceDataNode* manifestObject = parentNode->GetParent();
if (manifestObject && manifestObject->GetClassMetadata()->m_azRtti->IsTypeOf(DataTypes::IManifestObject::RTTI_Type()))
{
DataTypes::IManifestObject* owner = static_cast<DataTypes::IManifestObject*>(manifestObject->FirstInstance());
GUI->SetManifestVector(instance.begin(), instance.end(), owner);
}
else
{
AZ_TracePrintf(Utilities::WarningWindow, "RuleContainer requires a ManifestObject parent.");
}
}
else
{
AZ_TracePrintf(Utilities::WarningWindow, "ManifestVectorWidget requires a ManifestObject parent.");
}
}
else
{
AZ_TracePrintf(Utilities::WarningWindow, "ManifestVectorWidget requires valid parent with RTTI data specified");
}
return false;
}
template<typename ManifestType>
void IManifestVectorHandler<ManifestType>::Register()
{
if (!IManifestVectorHandler::s_instance)
{
IManifestVectorHandler::s_instance = aznew IManifestVectorHandler();
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, IManifestVectorHandler::s_instance);
EBUS_EVENT_RESULT(s_serializeContext, AZ::ComponentApplicationBus, GetSerializeContext);
AZ_Assert(s_serializeContext, "Serialization context not available");
}
}
template<typename ManifestType>
void IManifestVectorHandler<ManifestType>::Unregister()
{
if (IManifestVectorHandler::s_instance)
{
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, UnregisterPropertyType, IManifestVectorHandler::s_instance);
delete IManifestVectorHandler::s_instance;
IManifestVectorHandler::s_instance = nullptr;
}
}
void ManifestVectorHandler::Register()
{
IManifestVectorHandler<DataTypes::IManifestObject>::Register();
IManifestVectorHandler<DataTypes::IRule>::Register();
}
void ManifestVectorHandler::Unregister()
{
IManifestVectorHandler<DataTypes::IManifestObject>::Unregister();
IManifestVectorHandler<DataTypes::IRule>::Unregister();
}
} // UI
} // SceneAPI
} // AZ
@@ -0,0 +1,72 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <AzCore/Math/Crc.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <SceneAPI/SceneCore/DataTypes/IManifestObject.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestVectorWidget.h>
#endif
class QWidget;
namespace AZ
{
class SerializeContext;
namespace SceneAPI
{
namespace UI
{
class ManifestVectorHandler
{
public:
static void Register();
static void Unregister();
};
// This class only has two specializations, that are defined in the cpp file
template<typename ManifestType>
class IManifestVectorHandler
: public QObject
, public AzToolsFramework::PropertyHandler<AZStd::vector<AZStd::shared_ptr<ManifestType>>, ManifestVectorWidget>
{
static_assert((AZStd::is_base_of<DataTypes::IManifestObject, ManifestType>::value), "Manifest type class must inherit from DataTypes::IManifestObject");
public:
AZ_CLASS_ALLOCATOR_DECL
QWidget* CreateGUI(QWidget* parent) override;
u32 GetHandlerName() const override;
bool AutoDelete() const override;
void ConsumeAttribute(ManifestVectorWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, ManifestVectorWidget* GUI, typename IManifestVectorHandler::property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, ManifestVectorWidget* GUI, const typename IManifestVectorHandler::property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
static void Register();
static void Unregister();
private:
static SerializeContext* s_serializeContext;
static IManifestVectorHandler* s_instance;
};
} // UI
} // SceneAPI
} // AZ
@@ -0,0 +1,321 @@
/*
* 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 <QEvent>
#include <QMenu>
#include <QTimer>
#include <QMessageBox>
#include <RowWidgets/ui_ManifestVectorWidget.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/DataTypes/IManifestObject.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#include <SceneAPI/SceneUI/RowWidgets/ManifestVectorWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(ManifestVectorWidget, SystemAllocator, 0)
ManifestVectorWidget::ManifestVectorWidget(SerializeContext* serializeContext, QWidget* parent)
: QWidget(parent)
, m_serializeContext(serializeContext)
, m_propertyEditor(nullptr)
, m_ui(new Ui::ManifestVectorWidget())
, m_capSize(50)
{
m_ui->setupUi(this);
m_propertyEditor = new AzToolsFramework::ReflectedPropertyEditor(this);
m_propertyEditor->Setup(m_serializeContext, this, false, 175);
m_propertyEditor->show();
m_ui->m_mainLayout->insertWidget(1, m_propertyEditor);
m_ui->m_addObjectButton->setProperty("class", "FixedMenu");
connect(m_ui->m_addObjectButton, &QPushButton::pressed, this, &ManifestVectorWidget::DisplayAddPrompt);
// Add empty menu for visual consistency.
m_ui->m_addObjectButton->setMenu(new QMenu(this));
BusConnect();
}
ManifestVectorWidget::~ManifestVectorWidget()
{
BusDisconnect();
}
void ManifestVectorWidget::SetManifestVector(const ManifestVectorType& manifestVector, DataTypes::IManifestObject* ownerObject)
{
AZ_Assert(ownerObject, "ManifestVectorWidgets must be initialized with a non-null owner object.");
m_manifestVector = manifestVector;
m_ownerObject = ownerObject;
UpdatePropertyGrid();
}
ManifestVectorWidget::ManifestVectorType ManifestVectorWidget::GetManifestVector()
{
return m_manifestVector;
}
void ManifestVectorWidget::SetCollectionName(const AZStd::string& name)
{
m_ui->m_containerTitle->setText(name.c_str());
}
void ManifestVectorWidget::SetCapSize(size_t cap)
{
m_capSize = cap;
}
void ManifestVectorWidget::SetCollectionTypeName(const AZStd::string& typeName)
{
QString addString = QString("Add ") + typeName.c_str();
m_ui->m_addObjectButton->setText(addString);
}
bool ManifestVectorWidget::ContainsManifestObject(const DataTypes::IManifestObject* object) const
{
for (auto& containedObject : m_manifestVector)
{
if (containedObject.get() == object)
{
return true;
}
}
return false;
}
bool ManifestVectorWidget::RemoveManifestObject(const DataTypes::IManifestObject* object)
{
AZ_TraceContext("Remove object type", object->RTTI_GetTypeName());
for (auto it = m_manifestVector.begin(); it < m_manifestVector.end(); ++it)
{
if ((*it).get() == object)
{
object->OnUserRemoved();
m_manifestVector.erase(it);
QTimer::singleShot(0, this,
[this]()
{
UpdatePropertyGrid();
EmitObjectChanged(m_ownerObject);
});
return true;
}
}
AZ_TracePrintf(Utilities::WarningWindow, "Tried to remove an object that was not contained in the vector.");
return false;
}
void ManifestVectorWidget::DisplayAddPrompt()
{
Events::ManifestMetaInfo::ModifiersList availableManifestUUIDs;
ManifestWidget* root = ManifestWidget::FindRoot(this);
AZ_Assert(root, "ManifestVectorWidget is not a child of a ManifestWidget.");
if (!root)
{
return;
}
AZStd::shared_ptr<Containers::Scene> scene = root->GetScene();
if (!scene)
{
return;
}
EBUS_EVENT(Events::ManifestMetaInfoBus, GetAvailableModifiers, availableManifestUUIDs, *scene, *m_ownerObject);
AZ_TraceContext("Parent manifest object type", m_ownerObject->RTTI_GetTypeName());
QMenu* objectMenu = m_ui->m_addObjectButton->menu();
objectMenu->clear();
for (auto& manifestUUID : availableManifestUUIDs)
{
const AZ::SerializeContext::ClassData* manifestClassData = m_serializeContext->FindClassData(manifestUUID);
AZ_TraceContext("Child manifest object UUID", manifestUUID.ToString<AZStd::string>());
if (!manifestClassData)
{
AZ_TracePrintf(Utilities::WarningWindow, "Class data was not registered for class, it will not be available as an option");
continue;
}
AZStd::string displayName;
if (manifestClassData->m_editData && manifestClassData->m_editData->m_name[0] != '\0')
{
displayName = manifestClassData->m_editData->m_name;
}
else if(manifestClassData->m_name[0] != '\0')
{
displayName = manifestClassData->m_name;
}
else
{
AZ_TracePrintf(Utilities::WarningWindow, "Class data did not contain a human readable name for class, it will not be available as an option");
continue;
}
QAction* objectCreateAction = new QAction(displayName.c_str(), m_ui->m_addObjectButton);
connect(objectCreateAction, &QAction::triggered, this,
[this, manifestClassData, displayName]()
{
this->AddNewObject(manifestClassData->m_factory, displayName);
});
objectMenu->addAction(objectCreateAction);
}
}
void ManifestVectorWidget::AddNewObject(SerializeContext::IObjectFactory* factory, const AZStd::string& objectName)
{
if (m_manifestVector.size() >= m_capSize)
{
QMessageBox::warning(this, "Cap reached", QString("The %1 container reached its cap of %2 entries.\nPlease remove entries to free up space.").
arg(m_ui->m_containerTitle->text()).arg(m_capSize));
return;
}
AZ_TraceContext("Object name", objectName);
AZStd::shared_ptr<DataTypes::IManifestObject> newObject(static_cast<DataTypes::IManifestObject*>(factory->Create(objectName.c_str())));
if (newObject)
{
newObject->OnUserAdded();
}
ManifestWidget* root = ManifestWidget::FindRoot(this);
AZ_Assert(root, "ManifestVectorWidget is not a child of a ManifestWidget.");
if (!root)
{
return;
}
AZ_TraceContext("Object type", newObject->RTTI_GetTypeName());
AZStd::shared_ptr<Containers::Scene> scene = root->GetScene();
EBUS_EVENT(Events::ManifestMetaInfoBus, InitializeObject, *scene, *newObject);
m_manifestVector.push_back(newObject);
UpdatePropertyGrid();
EmitObjectChanged(m_ownerObject);
}
void ManifestVectorWidget::UpdatePropertyGrid()
{
QSignalBlocker(this);
m_propertyEditor->ClearInstances();
for (auto &object : m_manifestVector)
{
if(object)
{
m_propertyEditor->AddInstance(object.get(), object->RTTI_GetType());
}
}
m_propertyEditor->InvalidateAll();
m_propertyEditor->ExpandAll();
}
void ManifestVectorWidget::EmitObjectChanged(const DataTypes::IManifestObject* object)
{
emit valueChanged();
ManifestWidget* root = ManifestWidget::FindRoot(this);
AZ_Assert(root, "ManifestVectorWidget is not a child of a ManifestWidget.");
if (!root)
{
return;
}
AZStd::shared_ptr<Containers::Scene> scene = root->GetScene();
if (!scene)
{
return;
}
Events::ManifestMetaInfoBus::Broadcast(&Events::ManifestMetaInfoBus::Events::ObjectUpdated, *scene, object, this);
}
void ManifestVectorWidget::AfterPropertyModified(AzToolsFramework::InstanceDataNode* node)
{
if (node && node->GetParent())
{
AzToolsFramework::InstanceDataNode* owner = node->GetParent();
const AZ::SerializeContext::ClassData* classData = owner->GetClassMetadata();
if (classData && classData->m_azRtti)
{
const DataTypes::IManifestObject* cast = classData->m_azRtti->Cast<DataTypes::IManifestObject>(owner->FirstInstance());
if (cast)
{
AZ_Assert(AZStd::find_if(m_manifestVector.begin(), m_manifestVector.end(),
[cast](const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
return object.get() == cast;
}) != m_manifestVector.end(), "ManifestVectorWidget detected an update of a field it doesn't own.");
EmitObjectChanged(cast);
}
}
}
}
void ManifestVectorWidget::RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode* /*node*/, const QPoint& /*point*/)
{
}
void ManifestVectorWidget::BeforePropertyModified(AzToolsFramework::InstanceDataNode* /*node*/)
{
}
void ManifestVectorWidget::SetPropertyEditingActive(AzToolsFramework::InstanceDataNode* /*node*/)
{
}
void ManifestVectorWidget::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* /*node*/)
{
}
void ManifestVectorWidget::SealUndoStack()
{
}
void ManifestVectorWidget::ObjectUpdated([[maybe_unused]] const Containers::Scene& scene, const DataTypes::IManifestObject* target, void* sender)
{
if (sender != this && target != nullptr && m_propertyEditor)
{
if (AZStd::find_if(m_manifestVector.begin(), m_manifestVector.end(),
[target](const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
return object.get() == target;
}) != m_manifestVector.end())
{
m_propertyEditor->InvalidateAttributesAndValues();
}
}
}
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
#include <RowWidgets/moc_ManifestVectorWidget.cpp>
@@ -0,0 +1,127 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#endif
namespace AZStd
{
template<class T>
class shared_ptr;
}
namespace AzToolsFramework
{
class ReflectedPropertyEditor;
class InstanceDataNode;
}
namespace SerializeContext
{
class IObjectFactory;
}
namespace AZ
{
class SerializeContext;
namespace SceneAPI
{
namespace DataTypes
{
class IManifestObject;
class IGroup;
}
namespace SceneData
{
template<class T>
class VectorWrapper;
}
namespace UI
{
// QT space
namespace Ui
{
class ManifestVectorWidget;
}
class ManifestVectorWidget
: public QWidget
, public AzToolsFramework::IPropertyEditorNotify
, public Events::ManifestMetaInfoBus::Handler
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
using ManifestVectorType = AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject> >;
ManifestVectorWidget(SerializeContext* serializeContext, QWidget* parent);
~ManifestVectorWidget() override;
template<typename InputIterator>
void SetManifestVector(InputIterator first, InputIterator last, DataTypes::IManifestObject* ownerObject)
{
AZ_Assert(ownerObject, "ManifestVectorWidgets must be initialized with a non-null owner object.");
m_manifestVector.assign(first, last);
m_ownerObject = ownerObject;
UpdatePropertyGrid();
}
void SetManifestVector(const ManifestVectorType& manifestVector, DataTypes::IManifestObject* ownerObject);
ManifestVectorType GetManifestVector();
void SetCollectionName(const AZStd::string& name);
void SetCollectionTypeName(const AZStd::string& typeName);
// Sets the number of entries the user can add through this widget. It doesn't limit
// the amount of entries that can be stored.
void SetCapSize(size_t cap);
bool ContainsManifestObject(const DataTypes::IManifestObject* object) const;
bool RemoveManifestObject(const DataTypes::IManifestObject* object);
signals:
void valueChanged();
protected:
void DisplayAddPrompt();
void AddNewObject(SerializeContext::IObjectFactory* factory, const AZStd::string& typeName);
void UpdatePropertyGrid();
void UpdatePropertyGridSize();
void EmitObjectChanged(const DataTypes::IManifestObject* object);
// IPropertyEditorNotify
void AfterPropertyModified(AzToolsFramework::InstanceDataNode* /*node*/) override;
void RequestPropertyContextMenu(AzToolsFramework::InstanceDataNode* /*node*/, const QPoint& /*point*/) override;
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* /*node*/) override;
void SetPropertyEditingActive(AzToolsFramework::InstanceDataNode* /*node*/) override;
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* /*node*/) override;
void SealUndoStack() override;
// ManifestMetaInfoBus
void ObjectUpdated(const Containers::Scene& scene, const DataTypes::IManifestObject* target, void* sender) override;
SerializeContext* m_serializeContext;
AzToolsFramework::ReflectedPropertyEditor* m_propertyEditor;
QScopedPointer<Ui::ManifestVectorWidget> m_ui;
DataTypes::IManifestObject* m_ownerObject;
ManifestVectorType m_manifestVector;
size_t m_capSize;
};
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::UI::ManifestVectorWidget</class>
<widget class="QWidget" name="AZ::SceneAPI::UI::ManifestVectorWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>20</verstretch>
</sizepolicy>
</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>
<layout class="QVBoxLayout" name="m_mainLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QLabel" name="m_containerTitle">
<property name="text">
<string></string>
</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="QPushButton" name="m_addObjectButton">
<property name="text">
<string>Add Object</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,200 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneUI/RowWidgets/NodeListSelectionHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(NodeListSelectionHandler, SystemAllocator, 0)
NodeListSelectionHandler* NodeListSelectionHandler::s_instance = nullptr;
QWidget* NodeListSelectionHandler::CreateGUI(QWidget* parent)
{
NodeListSelectionWidget* instance = aznew NodeListSelectionWidget(parent);
connect(instance, &NodeListSelectionWidget::valueChanged, this,
[instance]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, instance);
});
return instance;
}
u32 NodeListSelectionHandler::GetHandlerName() const
{
return AZ_CRC("NodeListSelection", 0x45c54909);
}
bool NodeListSelectionHandler::AutoDelete() const
{
return false;
}
void NodeListSelectionHandler::ConsumeAttribute(NodeListSelectionWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
AZ_TraceContext("Attribute name", debugName);
if (attrib == AZ_CRC("DisabledOption", 0x6cd17278))
{
ConsumeDisabledOptionAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("ClassTypeIdFilter", 0x21c301f1))
{
ConsumeClassTypeIdAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("RequiresExactTypeId", 0x9adf9b0d))
{
ConsumeRequiredExactTypeIdAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("UseShortNames", 0xf6a37fd3))
{
ConsumeUseShortNameAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("ExcludeEndPoints", 0x53bd29cc))
{
ConsumeExcludeEndPointsAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("DefaultToDisabled", 0xa2d03bd1))
{
ConsumeDefaultToDisabledAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("ComboBoxEditable", 0x7ee76669))
{
bool editable;
if (attrValue->Read<bool>(editable))
{
widget->setEditable(editable);
}
}
}
void NodeListSelectionHandler::WriteGUIValuesIntoProperty(size_t /*index*/, NodeListSelectionWidget* GUI,
property_t& instance, AzToolsFramework::InstanceDataNode* /*node*/)
{
instance = static_cast<property_t>(GUI->GetCurrentSelection());
}
bool NodeListSelectionHandler::ReadValuesIntoGUI(size_t /*index*/, NodeListSelectionWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* /*node*/)
{
GUI->SetCurrentSelection(instance);
return false;
}
void NodeListSelectionHandler::Register()
{
if (!s_instance)
{
s_instance = aznew NodeListSelectionHandler();
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, s_instance);
}
}
void NodeListSelectionHandler::Unregister()
{
if (s_instance)
{
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, UnregisterPropertyType, s_instance);
delete s_instance;
s_instance = nullptr;
}
}
void NodeListSelectionHandler::ConsumeDisabledOptionAttribute(NodeListSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
AZStd::string disabledOption;
if (attrValue->Read<AZStd::string>(disabledOption))
{
widget->AddDisabledOption(AZStd::move(disabledOption));
}
else
{
AZ_Assert(false, "Failed to read string from 'DisabledOption' attribute.");
}
}
void NodeListSelectionHandler::ConsumeClassTypeIdAttribute(NodeListSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
Uuid classTypeId;
if (attrValue->Read<Uuid>(classTypeId))
{
widget->SetClassTypeId(classTypeId);
}
else
{
AZ_Assert(false, "Failed to read uuid from 'ClassTypeIdFilter' attribute.");
}
}
void NodeListSelectionHandler::ConsumeRequiredExactTypeIdAttribute(NodeListSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
bool exactMatch;
if (attrValue->Read<bool>(exactMatch))
{
widget->UseExactClassTypeMatch(exactMatch);
}
else
{
AZ_Assert(false, "Failed to read boolean from 'RequiresExactTypeId' attribute.");
}
}
void NodeListSelectionHandler::ConsumeUseShortNameAttribute(NodeListSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
bool use;
if (attrValue->Read<bool>(use))
{
widget->UseShortNames(use);
}
else
{
AZ_Assert(false, "Failed to read boolean from 'UseShortNames' attribute.");
}
}
void NodeListSelectionHandler::ConsumeExcludeEndPointsAttribute(NodeListSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
bool exclude;
if (attrValue->Read<bool>(exclude))
{
widget->ExcludeEndPoints(exclude);
}
else
{
AZ_Assert(false, "Failed to read boolean from 'ExcludeEndPoints' attribute.");
}
}
void NodeListSelectionHandler::ConsumeDefaultToDisabledAttribute(NodeListSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
bool defaultToDisabled;
if (attrValue->Read<bool>(defaultToDisabled))
{
widget->DefaultToDisabled(defaultToDisabled);
}
else
{
AZ_Assert(false, "Failed to read boolean from 'DefaultToDisabled' attribute.");
}
}
} // UI
} // SceneAPI
} // AZ
#include <RowWidgets/moc_NodeListSelectionHandler.cpp>
@@ -0,0 +1,95 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <AzCore/Math/Crc.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <SceneAPI/SceneUI/RowWidgets/NodeListSelectionWidget.h>
#endif
class QWidget;
/*
=============================================================
= Handler Documentation =
=============================================================
Handler Name: "NodeListSelection"
Available Attributes:
o "DisabledOption" - Option presented to the user as the first option which will generally be interpreted
as the default or disabled option. For instance, "Disable Vertex Coloring" as the
default for selecting available vertex coloring options.
o "ClassTypeIdFilter" - The UUID of the graph object class type to be listed. If not set all available graph
objects will be listed.
o "RequiresExactTypeId" - When 'ClassTypeIdFilter' is set setting this to true will cause only instances of
the exact class to be listed, otherwise any class derived from the given UUID will be used.
o "UseShortNames" - Whether or not to display the full scene graph path or only the short name.
o "ExcludeEndPoints" - Whether or not graph nodes marked as end-points should be considered for displaying.
o "DefaultToDisabled" - Whether or not the default option is the disabled option or the first entry if the value
hasn't not been set or has become invalid. This requires 'DisabledOption' to be set,
otherwise the first entry will be choosen.
*/
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
class NodeListSelectionHandler
: public QObject, public AzToolsFramework::PropertyHandler<AZStd::string, NodeListSelectionWidget>
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
QWidget* CreateGUI(QWidget* parent) override;
u32 GetHandlerName() const override;
bool AutoDelete() const;
void ConsumeAttribute(NodeListSelectionWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, NodeListSelectionWidget* GUI, property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, NodeListSelectionWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
static void Register();
static void Unregister();
protected:
virtual void ConsumeDisabledOptionAttribute(NodeListSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
virtual void ConsumeClassTypeIdAttribute(NodeListSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
virtual void ConsumeRequiredExactTypeIdAttribute(NodeListSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
virtual void ConsumeUseShortNameAttribute(NodeListSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
virtual void ConsumeExcludeEndPointsAttribute(NodeListSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
virtual void ConsumeDefaultToDisabledAttribute(NodeListSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
private:
static NodeListSelectionHandler* s_instance;
};
} // UI
} // SceneAPI
} // AZ
@@ -0,0 +1,273 @@
/*
* 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 <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <SceneAPI/SceneUI/RowWidgets/NodeListSelectionWidget.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(NodeListSelectionWidget, SystemAllocator, 0)
NodeListSelectionWidget::NodeListSelectionWidget(QWidget* parent)
: QComboBox(parent)
, m_classTypeId(Uuid::CreateNull())
, m_exactClassTypeMatch(false)
, m_hasDirtyList(true)
, m_useShortNames(false)
, m_excludeEndPoints(false)
, m_defaultToDisabled(false)
{
connect(this, &QComboBox::currentTextChanged, this, &NodeListSelectionWidget::OnTextChange);
}
void NodeListSelectionWidget::SetCurrentSelection(const AZStd::string& selection)
{
m_currentSelection = selection;
if (!m_hasDirtyList)
{
SetSelection();
}
}
AZStd::string NodeListSelectionWidget::GetCurrentSelection() const
{
return currentText().toStdString().c_str();
}
void NodeListSelectionWidget::AddDisabledOption(const AZStd::string& option)
{
m_disabledOption = option;
m_hasDirtyList = true;
}
void NodeListSelectionWidget::AddDisabledOption(AZStd::string&& option)
{
m_disabledOption = AZStd::move(option);
m_hasDirtyList = true;
}
const AZStd::string& NodeListSelectionWidget::GetDisabledOption() const
{
return m_disabledOption;
}
void NodeListSelectionWidget::UseShortNames(bool use)
{
m_useShortNames = use;
m_hasDirtyList = true;
}
void NodeListSelectionWidget::ExcludeEndPoints(bool exclude)
{
m_excludeEndPoints = exclude;
m_hasDirtyList = true;
}
void NodeListSelectionWidget::DefaultToDisabled(bool value)
{
m_defaultToDisabled = value;
m_hasDirtyList = true;
}
void NodeListSelectionWidget::SetClassTypeId(const Uuid& classTypeId)
{
m_classTypeId = classTypeId;
m_hasDirtyList = true;
}
void NodeListSelectionWidget::ClearClassTypeId()
{
m_classTypeId = Uuid::CreateNull();
m_hasDirtyList = true;
}
void NodeListSelectionWidget::UseExactClassTypeMatch(bool exactMatch)
{
m_exactClassTypeMatch = exactMatch;
m_hasDirtyList = true;
}
void NodeListSelectionWidget::OnTextChange(const QString& text)
{
if (!m_hasDirtyList)
{
m_currentSelection = text.toStdString().c_str();
emit valueChanged(m_currentSelection);
}
}
void NodeListSelectionWidget::showEvent(QShowEvent* event)
{
if (m_hasDirtyList)
{
clear();
ManifestWidget* mainWidget = ManifestWidget::FindRoot(this);
AZ_Assert(mainWidget, "NodeListSelectionWidget is not an (in)direct child of the ManifestWidget.");
if (!mainWidget)
{
return;
}
const Containers::SceneGraph& graph = mainWidget->GetScene()->GetGraph();
BuildList(graph);
AddDisabledOption();
SetSelection();
setEnabled(count() > 1);
m_hasDirtyList = false;
}
QComboBox::showEvent(event);
}
void NodeListSelectionWidget::BuildList(const Containers::SceneGraph& graph)
{
EntrySet entries;
auto view = Containers::Views::MakePairView(graph.GetNameStorage(), graph.GetContentStorage());
for (auto it = view.begin(); it != view.end(); ++it)
{
if (!it->second || it->first.GetPathLength() == 0)
{
continue;
}
if (!IsCorrectType(*it->second))
{
continue;
}
if (m_excludeEndPoints)
{
Containers::SceneGraph::NodeIndex index = graph.ConvertToNodeIndex(it.GetFirstIterator());
if (graph.IsNodeEndPoint(index))
{
continue;
}
}
AddEntry(entries, it->first);
}
}
bool NodeListSelectionWidget::IsCorrectType(const DataTypes::IGraphObject& object) const
{
if (m_classTypeId.IsNull())
{
return true;
}
if (m_exactClassTypeMatch)
{
return object.RTTI_GetType() == m_classTypeId;
}
else
{
return object.RTTI_IsTypeOf(m_classTypeId);
}
}
void NodeListSelectionWidget::AddEntry(EntrySet& entries, const Containers::SceneGraph::Name& name)
{
if (m_useShortNames)
{
const char* shortName = name.GetName();
if (entries.find(shortName) == entries.end())
{
entries.insert(shortName);
addItem(shortName);
}
}
else
{
const char* pathName = name.GetPath();
if (entries.find(pathName) == entries.end())
{
entries.insert(pathName);
addItem(pathName);
}
}
}
void NodeListSelectionWidget::SetSelection()
{
QString entryName = m_currentSelection.c_str();
int index = findText(entryName);
if (m_disabledOption.empty())
{
if (index >= 0)
{
setCurrentIndex(index);
}
else
{
if (!isEditable())
{
// If not freeform editable, and no disabled option available to set to first entry.
setCurrentIndex(0);
}
else
{
setEditText(entryName);
}
}
}
else
{
// Check against index 1 as an empty string will return the separator.
if (index >= 0 && index != 1)
{
setCurrentIndex(index);
}
else if (!m_defaultToDisabled && count() >= 2)
{
// Pick third option as the first is the default followed
// by the separator.
setCurrentIndex(2);
}
else if (!isEditable())
{
// If not editable and no match was found, set to first entry.
setCurrentIndex(0);
}
else
{
setEditText(entryName);
}
}
}
void NodeListSelectionWidget::AddDisabledOption()
{
if (!m_disabledOption.empty())
{
insertItem(0, m_disabledOption.c_str());
// Only add a separator if the disabled option isn't the only entry.
if (count() > 1)
{
insertSeparator(1);
}
}
}
} // UI
} // SceneAPI
} // AZ
#include <RowWidgets/moc_NodeListSelectionWidget.cpp>
@@ -0,0 +1,97 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QComboBox>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#endif
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class IGraphObject;
}
namespace UI
{
class NodeListSelectionWidget : public QComboBox
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
explicit NodeListSelectionWidget(QWidget* parent);
void SetCurrentSelection(const AZStd::string& selection);
AZStd::string GetCurrentSelection() const;
void AddDisabledOption(const AZStd::string& option);
void AddDisabledOption(AZStd::string&& option);
const AZStd::string& GetDisabledOption() const;
void UseShortNames(bool use);
// Sets the class type id to filter against.
void SetClassTypeId(const Uuid& classTypeId);
void ClearClassTypeId();
// When the classTypeId is set as this is true, the nodes it the tree
// must have the exact same type id, if set to false all types
// matching the type id and derived classes will be listed.
void UseExactClassTypeMatch(bool exactMatch);
void ExcludeEndPoints(bool exclude);
// If the assigned selection is missing the selection will default to
// the disabled value if present and true, otherwise the alphabetically
// first entry is used.
void DefaultToDisabled(bool value);
signals:
void valueChanged(const AZStd::string& newValue);
protected slots:
void OnTextChange(const QString& text);
protected:
using EntrySet = AZStd::unordered_set<AZStd::string>;
void showEvent(QShowEvent* event) override;
void BuildList(const Containers::SceneGraph& graph);
bool IsCorrectType(const DataTypes::IGraphObject& object) const;
void AddEntry(EntrySet& entries, const Containers::SceneGraph::Name& name);
void SetSelection();
void AddDisabledOption();
AZStd::string m_disabledOption;
AZStd::string m_currentSelection;
Uuid m_classTypeId;
// Set to true if only a specific class type should be in the filter, otherwise
// all classes that derive from the given type will be listed.
bool m_exactClassTypeMatch;
// Attributes come in after widget has been created and this requires the
// list to be rebuild. This flag keeps track of any changes and
// whether or not the list should be repopulated.
bool m_hasDirtyList;
bool m_useShortNames;
bool m_excludeEndPoints;
bool m_defaultToDisabled;
};
} // UI
} // SceneAPI
} // AZ
@@ -0,0 +1,172 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneUI/RowWidgets/NodeTreeSelectionHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(NodeTreeSelectionHandler, SystemAllocator, 0)
NodeTreeSelectionHandler* NodeTreeSelectionHandler::s_instance = nullptr;
QWidget* NodeTreeSelectionHandler::CreateGUI(QWidget* parent)
{
NodeTreeSelectionWidget* instance = aznew NodeTreeSelectionWidget(parent);
connect(instance, &NodeTreeSelectionWidget::valueChanged, this,
[instance]()
{
EBUS_EVENT(AzToolsFramework::PropertyEditorGUIMessages::Bus, RequestWrite, instance);
});
return instance;
}
u32 NodeTreeSelectionHandler::GetHandlerName() const
{
return AZ_CRC("NodeTreeSelection", 0x58649112);
}
bool NodeTreeSelectionHandler::AutoDelete() const
{
return false;
}
bool NodeTreeSelectionHandler::IsDefaultHandler() const
{
return true;
}
void NodeTreeSelectionHandler::ConsumeAttribute(NodeTreeSelectionWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
AZ_TraceContext("Attribute name", debugName);
if (attrib == AZ_CRC("FilterName", 0xf49ce62e))
{
ConsumeFilterNameAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("FilterType", 0x2661cf01))
{
ConsumeFilterTypeAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("FilterVirtualType", 0x972bc6a4))
{
ConsumeFilterVirtualTypeAttribute(widget, attrValue);
}
else if (attrib == AZ_CRC("NarrowSelection", 0xdc8002ec))
{
ConsumeNarrowSelectionAttribute(widget, attrValue);
}
}
void NodeTreeSelectionHandler::WriteGUIValuesIntoProperty(size_t /*index*/, NodeTreeSelectionWidget* GUI,
property_t& instance, AzToolsFramework::InstanceDataNode* /*node*/)
{
GUI->CopyListTo(instance);
GUI->UpdateSelectionLabel();
}
bool NodeTreeSelectionHandler::ReadValuesIntoGUI(size_t /*index*/, NodeTreeSelectionWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* /*node*/)
{
GUI->SetList(instance);
GUI->UpdateSelectionLabel();
return false;
}
void NodeTreeSelectionHandler::Register()
{
if (!s_instance)
{
s_instance = aznew NodeTreeSelectionHandler();
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, RegisterPropertyType, s_instance);
}
}
void NodeTreeSelectionHandler::Unregister()
{
if (s_instance)
{
EBUS_EVENT(AzToolsFramework::PropertyTypeRegistrationMessages::Bus, UnregisterPropertyType, s_instance);
delete s_instance;
s_instance = nullptr;
}
}
void NodeTreeSelectionHandler::ConsumeFilterNameAttribute(NodeTreeSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
AZStd::string filterName;
if (attrValue->Read<AZStd::string>(filterName))
{
widget->SetFilterName(AZStd::move(filterName));
}
else
{
AZ_Assert(false, "Failed to read string from 'FilterName' attribute.");
}
}
void NodeTreeSelectionHandler::ConsumeFilterTypeAttribute(NodeTreeSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
Uuid filterType;
if (attrValue->Read<Uuid>(filterType))
{
widget->AddFilterType(filterType);
}
else
{
AZ_Assert(false, "Failed to read Uuid from 'FilterType' attribute.");
}
}
void NodeTreeSelectionHandler::ConsumeFilterVirtualTypeAttribute(NodeTreeSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
Crc32 filterVirtualType;
if (!attrValue->Read<Crc32>(filterVirtualType))
{
AZStd::string filterFilterTypeName;
if (attrValue->Read<AZStd::string>(filterFilterTypeName))
{
filterVirtualType = Crc32(filterFilterTypeName.c_str());
}
else
{
AZ_Assert(false, "Failed to read crc value or string from 'VirtualFilterName' attribute.");
return;
}
}
widget->AddFilterVirtualType(filterVirtualType);
}
void NodeTreeSelectionHandler::ConsumeNarrowSelectionAttribute(NodeTreeSelectionWidget* widget, AzToolsFramework::PropertyAttributeReader* attrValue)
{
bool narrowSelection;
if (attrValue->Read<bool>(narrowSelection))
{
widget->UseNarrowSelection(narrowSelection);
}
else
{
AZ_Assert(false, "Failed to read boolean from 'NarrowSelection' attribute.");
}
}
} // UI
} // SceneAPI
} // AZ
#include <RowWidgets/moc_NodeTreeSelectionHandler.cpp>
@@ -0,0 +1,92 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <AzCore/Math/Crc.h>
#include <AzCore/std/string/string.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <SceneAPI/SceneUI/RowWidgets/NodeTreeSelectionWidget.h>
#include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
#endif
class QWidget;
/*
=============================================================
= Handler Documentation =
=============================================================
Handler Name: "NodeTreeSelection"
Available Attributes:
FilterName - Name of the filter type used in the summary label.
FilterType - Uuid for the type(s) to filter for. This attribute can be added multiple times. By default all
types will be considered but by adding one or more of filters only classes that match the uuid
of the given type or are derived of that type will be used for the selected and total count.
The object is an end-point it will also show in the selection graph, otherwise end-points are hidden.
FilterVirtualType - Crc32 or name (string) for the type(s) to filter for. This attribute can be added multiple
times. By default all types will be considered but by adding one or more of filters only objects
that match any of the virtual types will be used for the selected and total count.
The object is an end-point it will also show in the selection graph, otherwise end-points are hidden.
NarrowSelection - If set to true only filter types will have a checkbox, otherwise all entries can be selected.
*/
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_PUSH_DISABLE_DLL_EXPORT_BASECLASS_WARNING
class SCENE_UI_API NodeTreeSelectionHandler
: public QObject
, public AzToolsFramework::PropertyHandler<DataTypes::ISceneNodeSelectionList, NodeTreeSelectionWidget>
{
AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
QWidget* CreateGUI(QWidget* parent) override;
u32 GetHandlerName() const override;
bool AutoDelete() const override;
bool IsDefaultHandler() const override;
void ConsumeAttribute(NodeTreeSelectionWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, NodeTreeSelectionWidget* GUI, property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, NodeTreeSelectionWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
static void Register();
static void Unregister();
protected:
virtual void ConsumeFilterNameAttribute(NodeTreeSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
virtual void ConsumeFilterTypeAttribute(NodeTreeSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
virtual void ConsumeFilterVirtualTypeAttribute(NodeTreeSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
virtual void ConsumeNarrowSelectionAttribute(NodeTreeSelectionWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
private:
static NodeTreeSelectionHandler* s_instance;
};
} // UI
} // SceneAPI
} // AZ
@@ -0,0 +1,338 @@
/*
* 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 <QLabel>
#include <QToolButton>
#include <AzCore/std/bind/bind.h>
#include <RowWidgets/ui_NodeTreeSelectionWidget.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
#include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneUI/RowWidgets/NodeTreeSelectionWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(NodeTreeSelectionWidget, SystemAllocator, 0)
NodeTreeSelectionWidget::NodeTreeSelectionWidget(QWidget* parent)
: QWidget(parent)
, ui(new Ui::NodeTreeSelectionWidget())
, m_narrowSelection(false)
, m_filterName("nodes")
{
ui->setupUi(this);
ui->m_selectButton->setIcon(QIcon(":/SceneUI/Manifest/TreeIcon.png"));
connect(ui->m_selectButton, &QToolButton::clicked, this, &NodeTreeSelectionWidget::SelectButtonClicked);
}
NodeTreeSelectionWidget::~NodeTreeSelectionWidget() = default;
void NodeTreeSelectionWidget::SetList(const DataTypes::ISceneNodeSelectionList& list)
{
m_list = list.Copy();
}
void NodeTreeSelectionWidget::CopyListTo(DataTypes::ISceneNodeSelectionList& target)
{
if (m_list)
{
m_list->CopyTo(target);
}
}
void NodeTreeSelectionWidget::SetFilterName(const AZStd::string& name)
{
ui->m_selectButton->setToolTip(QString::asprintf("Select %s", name.c_str()));
m_filterName = name;
}
void NodeTreeSelectionWidget::SetFilterName(AZStd::string&& name)
{
ui->m_selectButton->setToolTip(QString::asprintf("Select %s", name.c_str()));
m_filterName = AZStd::move(name);
}
void NodeTreeSelectionWidget::AddFilterType(const Uuid& idProperty)
{
if (m_filterTypes.find(idProperty) == m_filterTypes.end())
{
m_filterTypes.insert(idProperty);
}
}
void NodeTreeSelectionWidget::AddFilterVirtualType(Crc32 name)
{
if (m_filterVirtualTypes.find(name) == m_filterVirtualTypes.end())
{
m_filterVirtualTypes.insert(name);
}
}
void NodeTreeSelectionWidget::UseNarrowSelection(bool enable)
{
m_narrowSelection = enable;
}
void NodeTreeSelectionWidget::SelectButtonClicked()
{
AZ_Assert(!m_treeWidget, "Node tree already active, NodeTreeSelectionWidget button pressed multiple times.");
AZ_Assert(m_list, "Requested updating of selection list before it was set.");
ManifestWidget* root = ManifestWidget::FindRoot(this);
AZ_Assert(root, "NodeTreeSelectionWidget is not a child of a ManifestWidget.");
if (!m_list || !root)
{
return;
}
AZStd::shared_ptr<Containers::Scene> scene = root->GetScene();
if (!scene)
{
return;
}
OverlayWidgetButtonList buttons;
OverlayWidgetButton acceptButton;
acceptButton.m_text = "Select";
acceptButton.m_callback = AZStd::bind(&NodeTreeSelectionWidget::ListChangesAccepted, this);
acceptButton.m_triggersPop = true;
OverlayWidgetButton cancelButton;
cancelButton.m_text = "Cancel";
cancelButton.m_callback = AZStd::bind(&NodeTreeSelectionWidget::ListChangesCanceled, this);
cancelButton.m_triggersPop = true;
cancelButton.m_isCloseButton = true;
buttons.push_back(&acceptButton);
buttons.push_back(&cancelButton);
ResetNewTreeWidget(*scene);
for (const Uuid& filterType : m_filterTypes)
{
m_treeWidget->AddFilterType(filterType);
}
for (Crc32 virtualTypeName : m_filterVirtualTypes)
{
m_treeWidget->AddVirtualFilterType(virtualTypeName);
}
if (m_narrowSelection)
{
m_treeWidget->MakeCheckable(SceneGraphWidget::CheckableOption::OnlyFilterTypesCheckable);
}
m_treeWidget->Build();
QLabel* label = new QLabel("Finish selecting nodes to continue editing settings.");
label->setAlignment(Qt::AlignCenter);
OverlayWidget::PushLayerToContainingOverlay(this, label, m_treeWidget.get(), "Select nodes", buttons);
}
void NodeTreeSelectionWidget::ResetNewTreeWidget(const Containers::Scene& scene)
{
m_treeWidget.reset(aznew SceneGraphWidget(scene, *m_list));
}
void NodeTreeSelectionWidget::ListChangesAccepted()
{
m_list = m_treeWidget->ClaimTargetList();
m_treeWidget.reset();
emit valueChanged();
}
void NodeTreeSelectionWidget::ListChangesCanceled()
{
m_treeWidget.reset();
}
void NodeTreeSelectionWidget::UpdateSelectionLabel()
{
if (m_list)
{
size_t selected = CalculateSelectedCount();
size_t total = CalculateTotalCount();
AZ_Assert(selected <= total, "Selected count of nodes should not be greater than the total count");
if (total == 0)
{
ui->m_statusLabel->setText("Default selection");
}
else if (selected == total)
{
ui->m_statusLabel->setText(QString::asprintf("All %s selected", m_filterName.c_str()));
}
else
{
ui->m_statusLabel->setText(
QString("%1 of %2 %3 selected").arg(selected).arg(total).arg(m_filterName.c_str()));
}
}
else
{
ui->m_statusLabel->setText("No list assigned");
}
}
size_t NodeTreeSelectionWidget::CalculateSelectedCount()
{
if (!m_list)
{
return 0;
}
const ManifestWidget* root = ManifestWidget::FindRoot(this);
AZ_Assert(root, "NodeTreeSelectionWidget is not a child of a ManifestWidget.");
if (!m_list || !root)
{
return 0;
}
const Containers::SceneGraph& graph = root->GetScene()->GetGraph();
size_t result = 0;
AZStd::unique_ptr<DataTypes::ISceneNodeSelectionList> tempList(m_list->Copy());
Utilities::SceneGraphSelector::UpdateNodeSelection(graph, *tempList);
size_t selectedCount = tempList->GetSelectedNodeCount();
for (size_t i = 0; i < selectedCount; ++i)
{
Containers::SceneGraph::NodeIndex index = graph.Find(tempList->GetSelectedNode(i));
if (!index.IsValid())
{
continue;
}
AZStd::shared_ptr<const DataTypes::IGraphObject> object = graph.GetNodeContent(index);
if (!object)
{
continue;
}
if (m_filterTypes.empty() && m_filterVirtualTypes.empty())
{
result++;
continue;
}
bool foundType = false;
for (const Uuid& type : m_filterTypes)
{
if (object->RTTI_IsTypeOf(type))
{
result++;
foundType = true;
break;
}
}
if (foundType)
{
continue;
}
// Check if the object is one of the registered virtual types.
AZStd::set<Crc32> virtualTypes;
Events::GraphMetaInfoBus::Broadcast(&Events::GraphMetaInfo::GetVirtualTypes, virtualTypes, *root->GetScene(), index);
for (Crc32 name : virtualTypes)
{
if (m_filterVirtualTypes.find(name) != m_filterVirtualTypes.end())
{
result++;
break;
}
}
}
return result;
}
size_t NodeTreeSelectionWidget::CalculateTotalCount()
{
const ManifestWidget* root = ManifestWidget::FindRoot(this);
AZ_Assert(root, "NodeTreeSelectionWidget is not a child of a ManifestWidget.");
if (!m_list || !root)
{
return 0;
}
const Containers::SceneGraph& graph = root->GetScene()->GetGraph();
size_t total = 0;
if (m_filterTypes.empty() && m_filterVirtualTypes.empty())
{
Containers::SceneGraph::HierarchyStorageConstData view = graph.GetHierarchyStorage();
if (!graph.GetNodeContent(graph.GetRoot()) && graph.GetNodeName(graph.GetRoot()).GetPathLength() == 0)
{
view = Containers::SceneGraph::HierarchyStorageConstData(view.begin() + 1, view.end());
}
for (auto& it : view)
{
if (!it.IsEndPoint())
{
total++;
}
}
}
else
{
for (auto it = graph.GetContentStorage().begin(); it != graph.GetContentStorage().end(); ++it)
{
if (!(*it))
{
continue;
}
Containers::SceneGraph::NodeIndex index = graph.ConvertToNodeIndex(it);
bool foundType = false;
for (const Uuid& type : m_filterTypes)
{
if ((*it)->RTTI_IsTypeOf(type))
{
total++;
foundType = true;
break;
}
}
if (foundType)
{
continue;
}
// Check if the object is one of the registered virtual types.
AZStd::set<Crc32> virtualTypes;
Events::GraphMetaInfoBus::Broadcast(&Events::GraphMetaInfo::GetVirtualTypes, virtualTypes, *root->GetScene(), index);
for (Crc32 name : virtualTypes)
{
if (m_filterVirtualTypes.find(name) != m_filterVirtualTypes.end())
{
total++;
break;
}
}
}
}
return total;
}
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
#include <RowWidgets/moc_NodeTreeSelectionWidget.cpp>
@@ -0,0 +1,87 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#include <SceneAPI/SceneUI/SceneWidgets/SceneGraphWidget.h>
#endif
namespace AZ
{
namespace SceneAPI
{
namespace DataTypes
{
class ISceneNodeSelectionList;
}
namespace UI
{
// QT space
namespace Ui
{
class NodeTreeSelectionWidget;
}
class SCENE_UI_API NodeTreeSelectionWidget : public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
explicit NodeTreeSelectionWidget(QWidget* parent);
~NodeTreeSelectionWidget() override;
void SetList(const DataTypes::ISceneNodeSelectionList& list);
void CopyListTo(DataTypes::ISceneNodeSelectionList& target);
void SetFilterName(const AZStd::string& name);
void SetFilterName(AZStd::string&& name);
void AddFilterType(const Uuid& idProperty);
void AddFilterVirtualType(Crc32 name);
void UseNarrowSelection(bool enable);
void UpdateSelectionLabel();
signals:
void valueChanged();
protected:
void SelectButtonClicked();
void ListChangesAccepted();
void ListChangesCanceled();
virtual void ResetNewTreeWidget(const Containers::Scene& scene);
size_t CalculateSelectedCount();
size_t CalculateTotalCount();
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZStd::set<Uuid> m_filterTypes;
AZStd::set<Crc32> m_filterVirtualTypes;
AZStd::string m_filterName;
QScopedPointer<Ui::NodeTreeSelectionWidget> ui;
AZStd::unique_ptr<SceneGraphWidget> m_treeWidget;
AZStd::unique_ptr<DataTypes::ISceneNodeSelectionList> m_list;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
bool m_narrowSelection;
};
} // UI
} // SceneAPI
} // AZ
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::UI::NodeTreeSelectionWidget</class>
<widget class="QWidget" name="AZ::SceneAPI::UI::NodeTreeSelectionWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</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="m_statusLabel">
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string>All nodes selected</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="m_selectButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
<property name="toolTip">
<string>Select Nodes</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,112 @@
/*
* 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 <AzCore/EBus/EBus.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyVectorCtrl.hxx>
#include <SceneAPI/SceneUI/RowWidgets/TransformRowHandler.h>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
AZ_CLASS_ALLOCATOR_IMPL(TranformRowHandler, SystemAllocator, 0)
TranformRowHandler* TranformRowHandler::s_instance = nullptr;
QWidget* TranformRowHandler::CreateGUI(QWidget* parent)
{
return aznew TransformRowWidget(parent);
}
u32 TranformRowHandler::GetHandlerName() const
{
return AZ_CRC("TranformRow", 0x795295be);
}
bool TranformRowHandler::AutoDelete() const
{
return false;
}
bool TranformRowHandler::IsDefaultHandler() const
{
return true;
}
void TranformRowHandler::ConsumeAttribute(TransformRowWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName)
{
if (attrib == AZ::Edit::Attributes::ReadOnly)
{
bool value;
if (attrValue->Read<bool>(value))
{
widget->SetEnableEdit(!value);
}
}
else
{
AzToolsFramework::Vector3PropertyHandler handler;
handler.ConsumeAttribute(widget->GetTranslationWidget(), attrib, attrValue, debugName);
handler.ConsumeAttribute(widget->GetRotationWidget(), attrib, attrValue, debugName);
handler.ConsumeAttribute(widget->GetScaleWidget(), attrib, attrValue, debugName);
}
}
void TranformRowHandler::WriteGUIValuesIntoProperty(size_t /*index*/, TransformRowWidget* GUI,
property_t& instance, AzToolsFramework::InstanceDataNode* /*node*/)
{
GUI->GetTransform(instance);
}
bool TranformRowHandler::ReadValuesIntoGUI(size_t /*index*/, TransformRowWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* /*node*/)
{
GUI->SetTransform(instance);
return false;
}
void TranformRowHandler::Register()
{
using namespace AzToolsFramework;
if (!s_instance)
{
s_instance = aznew TranformRowHandler();
PropertyTypeRegistrationMessages::Bus::Broadcast(&PropertyTypeRegistrationMessages::Bus::Events::RegisterPropertyType, s_instance);
}
}
void TranformRowHandler::Unregister()
{
using namespace AzToolsFramework;
if (s_instance)
{
PropertyTypeRegistrationMessages::Bus::Broadcast(&PropertyTypeRegistrationMessages::Bus::Events::UnregisterPropertyType, s_instance);
delete s_instance;
s_instance = nullptr;
}
}
void TranformRowHandler::ConsumeFilterTypeAttribute(TransformRowWidget* /*widget*/,
AzToolsFramework::PropertyAttributeReader* /*attrValue*/)
{
}
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
#include <RowWidgets/moc_TransformRowHandler.cpp>
@@ -0,0 +1,63 @@
/*
* 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/Math/Transform.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <SceneAPI/SceneUI/RowWidgets/TransformRowWidget.h>
#endif
class QWidget;
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
class TranformRowHandler
: public QObject
, public AzToolsFramework::PropertyHandler<AZ::Transform, TransformRowWidget>
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
QWidget* CreateGUI(QWidget* parent) override;
u32 GetHandlerName() const override;
bool AutoDelete() const;
bool IsDefaultHandler() const override;
void ConsumeAttribute(TransformRowWidget* widget, u32 attrib,
AzToolsFramework::PropertyAttributeReader* attrValue, const char* debugName) override;
void WriteGUIValuesIntoProperty(size_t index, TransformRowWidget* GUI, property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
bool ReadValuesIntoGUI(size_t index, TransformRowWidget* GUI, const property_t& instance,
AzToolsFramework::InstanceDataNode* node) override;
static void Register();
static void Unregister();
protected:
virtual void ConsumeFilterTypeAttribute(TransformRowWidget* widget,
AzToolsFramework::PropertyAttributeReader* attrValue);
private:
static TranformRowHandler* s_instance;
};
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,262 @@
/*
* 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 <QLabel>
#include <QStyle>
#include <QGridLayout>
#include <SceneAPI/SceneUI/RowWidgets/TransformRowWidget.h>
#include <AzQtComponents/Components/Widgets/VectorInput.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyRowWidget.hxx>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
AZ_CLASS_ALLOCATOR_IMPL(ExpandedTransform, SystemAllocator, 0);
void PopulateVector3(AzQtComponents::VectorInput* vectorProperty, AZ::Vector3& vector)
{
AZ_Assert(vectorProperty->getSize() == 3, "Trying to populate a Vector3 from an invalidly sized Vector PropertyCtrl");
if (vectorProperty->getSize() < 3)
{
return;
}
AzQtComponents::VectorElement** elements = vectorProperty->getElements();
for (int i = 0; i < vectorProperty->getSize(); ++i)
{
AzQtComponents::VectorElement* currentElement = elements[i];
vector.SetElement(i, aznumeric_cast<float>(currentElement->getValue()));
}
}
ExpandedTransform::ExpandedTransform()
: m_translation(0, 0, 0)
, m_rotation(0, 0, 0)
, m_scale(1, 1, 1)
{
}
ExpandedTransform::ExpandedTransform(const Transform& transform)
{
SetTransform(transform);
}
void ExpandedTransform::SetTransform(const AZ::Transform& transform)
{
m_translation = transform.GetTranslation();
m_rotation = transform.GetEulerDegrees();
m_scale = transform.GetScale();
}
void ExpandedTransform::GetTransform(AZ::Transform& transform) const
{
transform = Transform::CreateTranslation(m_translation);
transform *= AZ::ConvertEulerDegreesToTransform(m_rotation);
transform.MultiplyByScale(m_scale);
}
const AZ::Vector3& ExpandedTransform::GetTranslation() const
{
return m_translation;
}
void ExpandedTransform::SetTranslation(const AZ::Vector3& translation)
{
m_translation = translation;
}
const AZ::Vector3& ExpandedTransform::GetRotation() const
{
return m_rotation;
}
void ExpandedTransform::SetRotation(const AZ::Vector3& rotation)
{
m_rotation = rotation;
}
const AZ::Vector3& ExpandedTransform::GetScale() const
{
return m_scale;
}
void ExpandedTransform::SetScale(const AZ::Vector3& scale)
{
m_scale = scale;
}
AZ_CLASS_ALLOCATOR_IMPL(TransformRowWidget, SystemAllocator, 0);
TransformRowWidget::TransformRowWidget(QWidget* parent)
: QWidget(parent)
{
QWidget* hider = new QWidget();
QGridLayout* layout = new QGridLayout();
layout->setMargin(0);
QGridLayout* layout2 = new QGridLayout();
setLayout(layout);
AzToolsFramework::PropertyRowWidget* parentWidget = reinterpret_cast<AzToolsFramework::PropertyRowWidget*>(parent);
QToolButton* toolButton = parentWidget->GetIndicatorButton();
QVBoxLayout* layoutOriginal = parentWidget->GetLeftHandSideLayoutParent();
parentWidget->SetAsCustom(true);
parentWidget->GetNameLabel()->setContentsMargins(0, 0, 0, 0);
m_translationWidget = new AzQtComponents::VectorInput(this, 3);
m_translationWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_translationWidget->setMinimum(-9999999);
m_translationWidget->setMaximum(9999999);
m_rotationWidget = new AzQtComponents::VectorInput(this, 3);
m_rotationWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_rotationWidget->setLabel(0, "P");
m_rotationWidget->setLabel(1, "R");
m_rotationWidget->setLabel(2, "Y");
m_rotationWidget->setMinimum(0);
m_rotationWidget->setMaximum(360);
m_rotationWidget->setSuffix(" degrees");
m_scaleWidget = new AzQtComponents::VectorInput(this, 3);
m_scaleWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_scaleWidget->setMinimum(0);
m_scaleWidget->setMaximum(10000);
layout2->addWidget(new AzQtComponents::ElidingLabel("Position"), 0, 1);
layout->addWidget(m_translationWidget, 1, 1);
layout2->addWidget(new AzQtComponents::ElidingLabel("Rotation"), 1, 1);
layout->addWidget(m_rotationWidget, 2, 1);
layout2->addWidget(new AzQtComponents::ElidingLabel("Scale"), 2, 1);
layout->addWidget(m_scaleWidget, 3, 1);
layout->setRowMinimumHeight(0,16);
layout2->setColumnMinimumWidth(0, 30);
toolButton->setArrowType(Qt::DownArrow);
parentWidget->SetIndentSize(1);
toolButton->setVisible(true);
hider->setLayout(layout2);
layoutOriginal->addWidget(hider);
connect(toolButton, &QToolButton::clicked, this, [this, hider, parentWidget, toolButton]
{
m_expanded = !m_expanded;
if (m_expanded)
{
this->show();
hider->show();
toolButton->setArrowType(Qt::DownArrow);
}
else
{
this->hide();
hider->hide();
toolButton->setArrowType(Qt::RightArrow);
}
});
QObject::connect(m_translationWidget, &AzQtComponents::VectorInput::valueChanged, this, [this]
{
AzQtComponents::VectorInput* widget = this->GetTranslationWidget();
AZ::Vector3 translation;
PopulateVector3(widget, translation);
m_transform.SetTranslation(translation);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, this);
});
QObject::connect(m_rotationWidget, &AzQtComponents::VectorInput::valueChanged, this, [this]
{
AzQtComponents::VectorInput* widget = this->GetRotationWidget();
AZ::Vector3 rotation;
PopulateVector3(widget, rotation);
m_transform.SetRotation(rotation);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, this);
});
QObject::connect(m_scaleWidget, &AzQtComponents::VectorInput::valueChanged, this, [this]
{
AzQtComponents::VectorInput* widget = this->GetScaleWidget();
AZ::Vector3 scale;
PopulateVector3(widget, scale);
m_transform.SetScale(scale);
AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestWrite, this);
});
}
void TransformRowWidget::SetEnableEdit(bool enableEdit)
{
m_translationWidget->setEnabled(enableEdit);
m_rotationWidget->setEnabled(enableEdit);
m_scaleWidget->setEnabled(enableEdit);
}
void TransformRowWidget::SetTransform(const AZ::Transform& transform)
{
blockSignals(true);
m_transform.SetTransform(transform);
m_translationWidget->setValuebyIndex(m_transform.GetTranslation().GetX(), 0);
m_translationWidget->setValuebyIndex(m_transform.GetTranslation().GetY(), 1);
m_translationWidget->setValuebyIndex(m_transform.GetTranslation().GetZ(), 2);
m_rotationWidget->setValuebyIndex(m_transform.GetRotation().GetX(), 0);
m_rotationWidget->setValuebyIndex(m_transform.GetRotation().GetY(), 1);
m_rotationWidget->setValuebyIndex(m_transform.GetRotation().GetZ(), 2);
m_scaleWidget->setValuebyIndex(m_transform.GetScale().GetX(), 0);
m_scaleWidget->setValuebyIndex(m_transform.GetScale().GetY(), 1);
m_scaleWidget->setValuebyIndex(m_transform.GetScale().GetZ(), 2);
blockSignals(false);
}
void TransformRowWidget::GetTransform(AZ::Transform& transform) const
{
m_transform.GetTransform(transform);
}
const ExpandedTransform& TransformRowWidget::GetExpandedTransform() const
{
return m_transform;
}
AzQtComponents::VectorInput* TransformRowWidget::GetTranslationWidget()
{
return m_translationWidget;
}
AzQtComponents::VectorInput* TransformRowWidget::GetRotationWidget()
{
return m_rotationWidget;
}
AzQtComponents::VectorInput* TransformRowWidget::GetScaleWidget()
{
return m_scaleWidget;
}
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
#include <RowWidgets/moc_TransformRowWidget.cpp>
@@ -0,0 +1,94 @@
/*
* 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 <QString>
#include <QLineEdit>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
namespace AzQtComponents
{
class VectorInput;
}
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
class SCENE_UI_API ExpandedTransform
{
public:
AZ_CLASS_ALLOCATOR_DECL;
ExpandedTransform();
explicit ExpandedTransform(const Transform& transform);
void GetTransform(AZ::Transform& transform) const;
void SetTransform(const AZ::Transform& transform);
const AZ::Vector3& GetTranslation() const;
void SetTranslation(const AZ::Vector3& translation);
const AZ::Vector3& GetRotation() const;
void SetRotation(const AZ::Vector3& translation);
const AZ::Vector3& GetScale() const;
void SetScale(const AZ::Vector3& scale);
private:
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZ::Vector3 m_translation;
AZ::Vector3 m_rotation;
AZ::Vector3 m_scale;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
};
class TransformRowWidget : public QWidget
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL;
explicit TransformRowWidget(QWidget* parent = nullptr);
void SetEnableEdit(bool enableEdit);
void SetTransform(const AZ::Transform& transform);
void GetTransform(AZ::Transform& transform) const;
const ExpandedTransform& GetExpandedTransform() const;
AzQtComponents::VectorInput* GetTranslationWidget();
AzQtComponents::VectorInput* GetRotationWidget();
AzQtComponents::VectorInput* GetScaleWidget();
protected:
ExpandedTransform m_transform;
bool m_expanded = true;
AzQtComponents::VectorInput* m_translationWidget;
AzQtComponents::VectorInput* m_rotationWidget;
AzQtComponents::VectorInput* m_scaleWidget;
};
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
+21
View File
@@ -0,0 +1,21 @@
<RCC>
<qresource prefix="/SceneUI/Graph">
<file alias="MeshIcon.png">../../../../Editor/Icons/AssetImporter/mesh_on.png</file>
<file alias="AnimationIcon.png">../../../../Editor/Icons/AssetImporter/animation_on.png</file>
<file alias="BoneIcon.png">../../../../Editor/Icons/AssetImporter/skeleton_on.png</file>
</qresource>
<qresource prefix="/SceneUI/Manifest">
<file alias="TreeIcon.png">../../../../Editor/Icons/AssetImporter/tree.png</file>
<file alias="MeshGroupIcon.png">../../../../Editor/Icons/AssetImporter/mesh.png</file>
<file alias="AnimationGroupIcon.png">../../../../Editor/Icons/AssetImporter/animation.png</file>
<file alias="SkeletonGroupIcon.png">../../../../Editor/Icons/AssetImporter/skeleton.png</file>
<file alias="SkinGroupIcon.png">../../../../Editor/Icons/AssetImporter/skin.png</file>
</qresource>
<qresource prefix="/SceneUI/Common">
<file alias="WarningIcon.png">../../../../Editor/Styles/StyleSheetImages/info_icon.png</file>
<file alias="ErrorIcon.png">../../../../Editor/Styles/StyleSheetImages/error_icon.png</file>
<file alias="AssertIcon.png">../../../../Editor/Icons/Assets/Lua.png</file>
<file alias="ExpandIcon.png">../../../../Editor/Icons/PropertyEditor/group_closed.png</file>
<file alias="CollapseIcon.png">../../../../Editor/Icons/PropertyEditor/group_open.png</file>
</qresource>
</RCC>
@@ -0,0 +1,25 @@
#pragma once
/*
* 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 <AzCore/PlatformDef.h>
#if defined(_LIB)
#define SCENE_UI_API
#else
#ifdef SCENE_UI_EXPORTS
#define SCENE_UI_API AZ_DLL_EXPORT
#else
#define SCENE_UI_API AZ_DLL_IMPORT
#endif
#endif
@@ -0,0 +1,40 @@
/*
* 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 <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneUI/SceneUIStandaloneAllocator.h>
namespace AZ
{
namespace SceneAPI
{
bool SceneUIStandaloneAllocator::m_allocatorInitialized = false;
void SceneUIStandaloneAllocator::Initialize()
{
if (!AZ::AllocatorInstance<AZ::SystemAllocator>().IsReady())
{
AZ::AllocatorInstance<AZ::SystemAllocator>().Create();
m_allocatorInitialized = true;
}
}
void SceneUIStandaloneAllocator::TearDown()
{
if (m_allocatorInitialized)
{
AZ::AllocatorInstance<AZ::SystemAllocator>().Destroy();
}
}
}
}
@@ -0,0 +1,31 @@
#pragma once
/*
* 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 <SceneAPI/SceneUI/SceneUIConfiguration.h>
namespace AZ
{
namespace SceneAPI
{
class SceneUIStandaloneAllocator
{
public:
SCENE_UI_API static void Initialize();
SCENE_UI_API static void TearDown();
private:
static bool m_allocatorInitialized;
};
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,76 @@
#
# 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.
#
set(FILES
SceneUIConfiguration.h
SceneUI.qrc
ManifestMetaInfoHandler.h
ManifestMetaInfoHandler.cpp
GraphMetaInfoHandler.h
GraphMetaInfoHandler.cpp
SceneUIStandaloneAllocator.h
SceneUIStandaloneAllocator.cpp
CommonWidgets/OverlayWidget.h
CommonWidgets/OverlayWidgetLayer.h
CommonWidgets/JobWatcher.h
CommonWidgets/JobWatcher.cpp
CommonWidgets/ProcessingOverlayWidget.h
CommonWidgets/ProcessingOverlayWidget.cpp
CommonWidgets/ProcessingOverlayWidget.ui
CommonWidgets/ExpandCollapseToggler.h
CommonWidgets/ExpandCollapseToggler.cpp
Handlers/ProcessingHandlers/ProcessingHandler.h
Handlers/ProcessingHandlers/ProcessingHandler.cpp
Handlers/ProcessingHandlers/ExportJobProcessingHandler.h
Handlers/ProcessingHandlers/ExportJobProcessingHandler.cpp
Handlers/ProcessingHandlers/AsyncOperationProcessingHandler.h
Handlers/ProcessingHandlers/AsyncOperationProcessingHandler.cpp
SceneWidgets/ManifestWidget.h
SceneWidgets/ManifestWidget.cpp
SceneWidgets/ManifestWidget.ui
SceneWidgets/ManifestWidgetPage.h
SceneWidgets/ManifestWidgetPage.cpp
SceneWidgets/ManifestWidgetPage.ui
SceneWidgets/SceneGraphWidget.h
SceneWidgets/SceneGraphWidget.cpp
SceneWidgets/SceneGraphWidget.ui
SceneWidgets/SceneGraphInspectWidget.h
SceneWidgets/SceneGraphInspectWidget.cpp
SceneWidgets/SceneGraphInspectWidget.ui
RowWidgets/HeaderWidget.h
RowWidgets/HeaderWidget.cpp
RowWidgets/HeaderWidget.ui
RowWidgets/HeaderHandler.h
RowWidgets/HeaderHandler.cpp
RowWidgets/ManifestVectorHandler.h
RowWidgets/ManifestVectorHandler.cpp
RowWidgets/ManifestVectorWidget.ui
RowWidgets/ManifestVectorWidget.h
RowWidgets/ManifestVectorWidget.cpp
RowWidgets/NodeListSelectionHandler.h
RowWidgets/NodeListSelectionHandler.cpp
RowWidgets/NodeListSelectionWidget.h
RowWidgets/NodeListSelectionWidget.cpp
RowWidgets/NodeTreeSelectionHandler.h
RowWidgets/NodeTreeSelectionHandler.cpp
RowWidgets/NodeTreeSelectionWidget.h
RowWidgets/NodeTreeSelectionWidget.cpp
RowWidgets/NodeTreeSelectionWidget.ui
RowWidgets/ManifestNameHandler.h
RowWidgets/ManifestNameHandler.cpp
RowWidgets/ManifestNameWidget.h
RowWidgets/ManifestNameWidget.cpp
RowWidgets/TransformRowHandler.h
RowWidgets/TransformRowHandler.cpp
RowWidgets/TransformRowWidget.h
RowWidgets/TransformRowWidget.cpp
DllMain.cpp
)
@@ -0,0 +1,15 @@
#
# 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.
#
set(FILES
Tests/TestsMain.cpp
Tests/RowWidgets/TransformRowWidgetTests.cpp
)
@@ -0,0 +1,205 @@
/*
* 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 <QLabel>
#include <QPushButton>
#include <SceneWidgets/ui_ManifestWidget.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidgetPage.h>
#include <SceneAPI/SceneUI/CommonWidgets/OverlayWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/SceneGraphWidget.h>
#include <AzCore/std/sort.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
ManifestWidget::ManifestWidget(SerializeContext* serializeContext, QWidget* parent)
: QWidget(parent)
, ui(new Ui::ManifestWidget())
, m_serializeContext(serializeContext)
{
ui->setupUi(this);
AzQtComponents::TabWidget::applySecondaryStyle(ui->m_tabs, false);
}
ManifestWidget::~ManifestWidget()
{
}
void ManifestWidget::BuildFromScene(const AZStd::shared_ptr<Containers::Scene>& scene)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
ui->m_tabs->clear();
m_pages.clear();
m_scene = scene;
if (!scene)
{
return;
}
BuildPages();
Containers::SceneManifest& manifest = scene->GetManifest();
for (auto& value : manifest.GetValueStorage())
{
AddObject(value);
}
for (ManifestWidgetPage* page : m_pages)
{
page->RefreshPage();
}
// Make sure to reset the active tab if the active tab is now empty
ManifestWidgetPage* currentPage = qobject_cast<ManifestWidgetPage*>(ui->m_tabs->currentWidget());
if (currentPage == nullptr || currentPage->ObjectCount() == 0)
{
for (ManifestWidgetPage* page : m_pages)
{
if (page->ObjectCount() > 0)
{
ui->m_tabs->setCurrentWidget(page);
break;
}
}
}
}
bool ManifestWidget::AddObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
for (ManifestWidgetPage* page : m_pages)
{
if (page->SupportsType(object))
{
return page->AddObject(object);
}
}
return false;
}
bool ManifestWidget::RemoveObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
for (ManifestWidgetPage* page : m_pages)
{
if (page->SupportsType(object))
{
return page->RemoveObject(object);
}
}
return false;
}
AZStd::shared_ptr<Containers::Scene> ManifestWidget::GetScene()
{
return m_scene;
}
AZStd::shared_ptr<const Containers::Scene> ManifestWidget::GetScene() const
{
return m_scene;
}
ManifestWidget* ManifestWidget::FindRoot(QWidget* child)
{
while (child != nullptr)
{
ManifestWidget* manifestWidget = qobject_cast<ManifestWidget*>(child);
if (manifestWidget)
{
return manifestWidget;
}
else
{
child = child->parentWidget();
}
}
return nullptr;
}
const ManifestWidget* ManifestWidget::FindRoot(const QWidget* child)
{
while (child != nullptr)
{
const ManifestWidget* manifestWidget = qobject_cast<const ManifestWidget*>(child);
if (manifestWidget)
{
return manifestWidget;
}
else
{
child = child->parentWidget();
}
}
return nullptr;
}
void ManifestWidget::BuildPages()
{
if (!m_scene)
{
return;
}
Events::ManifestMetaInfo::CategoryRegistrationList categories;
EBUS_EVENT(Events::ManifestMetaInfoBus, GetCategoryAssignments, categories, *m_scene);
AZStd::sort(categories.begin(), categories.end(),
[](const Events::ManifestMetaInfo::CategoryRegistration& lhs, const Events::ManifestMetaInfo::CategoryRegistration& rhs)
{
return (rhs.m_preferredOrder - lhs.m_preferredOrder) > 0;
}
);
AZStd::string currentCategory;
AZStd::vector<AZ::Uuid> types;
for (auto& category : categories)
{
if (category.m_categoryName != currentCategory)
{
// Skip first occurrence.
if (!currentCategory.empty())
{
ManifestWidgetPage* page = new ManifestWidgetPage(m_serializeContext, AZStd::move(types));
AddPage(currentCategory.c_str(), page);
}
currentCategory = category.m_categoryName;
AZ_Assert(types.empty(), "Expecting vectors to be empty after being moved.");
}
types.push_back(category.m_categoryTargetGroupId);
}
// Add final page
if (!currentCategory.empty())
{
ManifestWidgetPage* page = new ManifestWidgetPage(m_serializeContext, AZStd::move(types));
AddPage(currentCategory.c_str(), page);
}
}
void ManifestWidget::AddPage(const QString& category, ManifestWidgetPage* page)
{
m_pages.push_back(page);
ui->m_tabs->addTab(page, category);
}
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
#include <SceneWidgets/moc_ManifestWidget.cpp>
@@ -0,0 +1,82 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <QTabWidget>
#include <AzCore/std/string/string.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/containers/unordered_map.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
namespace AZ
{
class SerializeContext;
namespace SceneAPI
{
namespace Containers
{
class Scene;
}
namespace DataTypes
{
class IManifestObject;
}
namespace UI
{
// QT space
namespace Ui
{
class ManifestWidget;
}
class ManifestWidgetPage;
class SCENE_UI_API ManifestWidget : public QWidget
{
Q_OBJECT
public:
using PageList = AZStd::vector<ManifestWidgetPage*>;
explicit ManifestWidget(SerializeContext* serializeContext, QWidget* parent = nullptr);
~ManifestWidget() override;
void BuildFromScene(const AZStd::shared_ptr<Containers::Scene>& scene);
bool AddObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object);
bool RemoveObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object);
AZStd::shared_ptr<Containers::Scene> GetScene();
AZStd::shared_ptr<const Containers::Scene> GetScene() const;
//! Finds this ManifestWidget if the given widget is it's child, otherwise returns null.
static ManifestWidget* FindRoot(QWidget* child);
//! Finds this ManifestWidget if the given widget is it's child, otherwise returns null.
static const ManifestWidget* FindRoot(const QWidget* child);
protected:
void BuildPages();
void AddPage(const QString& category, ManifestWidgetPage* page);
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
PageList m_pages;
QScopedPointer<Ui::ManifestWidget> ui;
AZStd::shared_ptr<Containers::Scene> m_scene;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
SerializeContext* m_serializeContext;
};
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::UI::ManifestWidget</class>
<widget class="QWidget" name="AZ::SceneAPI::UI::ManifestWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="mainLayout">
<item>
<widget class="AzQtComponents::TabWidget" name="m_tabs"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AzQtComponents::TabWidget</class>
<extends>QTabWidget</extends>
<header>AzQtComponents/Components/Widgets/TabWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,426 @@
/*
* 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 <QMenu>
#include <QTimer>
#include <QScrollArea>
#include <QScrollBar>
#include <QMessageBox>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzCore/std/string/conversions.h>
#include <SceneWidgets/ui_ManifestWidgetPage.h>
#include <SceneAPI/SceneCore/DataTypes/IManifestObject.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/SceneManifest.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/ManifestWidgetPage.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
ManifestWidgetPage::ManifestWidgetPage(SerializeContext* context, AZStd::vector<AZ::Uuid>&& classTypeIds)
: m_classTypeIds(AZStd::move(classTypeIds))
, ui(new Ui::ManifestWidgetPage())
, m_propertyEditor(nullptr)
, m_context(context)
, m_capSize(100)
{
ui->setupUi(this);
m_propertyEditor = new AzToolsFramework::ReflectedPropertyEditor(nullptr);
m_propertyEditor->Setup(context, this, true, 250);
ui->m_mainLayout->insertWidget(0, m_propertyEditor);
BuildAndConnectAddButton();
BusConnect();
}
ManifestWidgetPage::~ManifestWidgetPage()
{
BusDisconnect();
}
void ManifestWidgetPage::SetCapSize(size_t size)
{
m_capSize = size;
}
size_t ManifestWidgetPage::GetCapSize() const
{
return m_capSize;
}
bool ManifestWidgetPage::SupportsType(const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
for (Uuid& id : m_classTypeIds)
{
if (object->RTTI_IsTypeOf(id))
{
return true;
}
}
return false;
}
bool ManifestWidgetPage::AddObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
if (!SupportsType(object))
{
return false;
}
if (!m_propertyEditor->AddInstance(object.get(), object->RTTI_GetType()))
{
AZ_Assert(false, "Failed to add manifest object to Reflected Property Editor.");
return false;
}
// Add new object to the list so it's ready for updating later on.
m_objects.push_back(object);
QTimer::singleShot(0, this,
[this]()
{
ScrollToBottom();
}
);
return true;
}
bool ManifestWidgetPage::RemoveObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
if (SupportsType(object))
{
// Explicitly keep a copy of the shared pointer to guarantee that the manifest object isn't
// deleted before it can be queued for the delete deletion.
AZStd::shared_ptr<DataTypes::IManifestObject> temp = object;
(void)temp;
auto it = AZStd::find(m_objects.begin(), m_objects.end(), object);
if (it == m_objects.end())
{
AZ_Assert(false, "Manifest object not part of manifest page.");
return false;
}
m_objects.erase(it);
if (m_objects.size() == 0)
{
// We won't get property modified event if it's the last element removed
EmitObjectChanged();
}
// If the property editor is immediately updated here QT will do some processing in an unexpected order,
// leading to heap corruption. To avoid this, keep a cached version of the deleted object and
// delay the rebuilding of the property editor to the end of the update cycle.
QTimer::singleShot(0, this,
[this, object]()
{
// Explicitly keep a copy of the shared pointer to guarantee that the manifest object isn't
// deleted between updates of QT.
(void)object;
m_propertyEditor->ClearInstances();
for (auto& instance : m_objects)
{
if (!m_propertyEditor->AddInstance(instance.get(), instance->RTTI_GetType()))
{
AZ_Assert(false, "Failed to add manifest object to Reflected Property Editor.");
}
}
RefreshPage();
}
);
return true;
}
else
{
return false;
}
}
size_t ManifestWidgetPage::ObjectCount() const
{
return m_objects.size();
}
void ManifestWidgetPage::Clear()
{
m_objects.clear();
m_propertyEditor->ClearInstances();
}
void ManifestWidgetPage::BeforePropertyModified(AzToolsFramework::InstanceDataNode* /*pNode*/)
{
}
void ManifestWidgetPage::AfterPropertyModified(AzToolsFramework::InstanceDataNode* node)
{
if (node)
{
while (node = node->GetParent())
{
if (const AZ::SerializeContext::ClassData* classData = node->GetClassMetadata(); classData && classData->m_azRtti)
{
if (const DataTypes::IManifestObject* cast = classData->m_azRtti->Cast<DataTypes::IManifestObject>(node->FirstInstance()); cast)
{
AZ_Assert(AZStd::find_if(m_objects.begin(), m_objects.end(),
[cast](const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
return object.get() == cast;
}) != m_objects.end(), "ManifestWidgetPage detected an update of a field it doesn't own.");
EmitObjectChanged(cast);
break;
}
}
}
}
}
void ManifestWidgetPage::SetPropertyEditingActive(AzToolsFramework::InstanceDataNode* /*pNode*/)
{
}
void ManifestWidgetPage::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* /*pNode*/)
{
}
void ManifestWidgetPage::SealUndoStack()
{
}
void ManifestWidgetPage::ScrollToBottom()
{
QScrollArea* propertyGridScrollArea = m_propertyEditor->findChild<QScrollArea*>();
if (propertyGridScrollArea)
{
propertyGridScrollArea->verticalScrollBar()->setSliderPosition(propertyGridScrollArea->verticalScrollBar()->maximum());
}
}
void ManifestWidgetPage::RefreshPage()
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Editor);
m_propertyEditor->InvalidateAll();
m_propertyEditor->ExpandAll();
}
void ManifestWidgetPage::OnSingleGroupAdd()
{
if (m_classTypeIds.size() > 0)
{
if (m_objects.size() >= m_capSize)
{
QMessageBox::warning(this, "Cap reached", QString("The group container reached its cap of %1 entries.\nPlease remove groups to free up space.").
arg(m_capSize));
return;
}
AddNewObject(m_classTypeIds[0]);
}
}
void ManifestWidgetPage::OnMultiGroupAdd(const Uuid& id)
{
if (m_objects.size() >= m_capSize)
{
QMessageBox::warning(this, "Cap reached", QString("The group container reached its cap of %1 entries.\nPlease remove groups to free up space.").
arg(m_capSize));
return;
}
AddNewObject(id);
}
void ManifestWidgetPage::BuildAndConnectAddButton()
{
if (m_classTypeIds.size() == 0)
{
ui->m_addButton->setText("No types for this group");
}
else if (m_classTypeIds.size() == 1)
{
AZStd::string className = ClassIdToName(m_classTypeIds[0]);
AZStd::to_lower(className.begin(), className.end());
ui->m_addButton->setText(QString::fromLatin1("Add another %1").arg(className.c_str()));
connect(ui->m_addButton, &QPushButton::clicked, this, &ManifestWidgetPage::OnSingleGroupAdd);
}
else
{
QMenu* menu = new QMenu();
AZStd::vector<AZStd::string> classNames;
for (Uuid& id : m_classTypeIds)
{
AZStd::string className = ClassIdToName(id);
menu->addAction(className.c_str(),
[this, id]()
{
OnMultiGroupAdd(id);
}
);
AZStd::to_lower(className.begin(), className.end());
classNames.push_back(className);
}
connect(menu, &QMenu::aboutToShow, this,
[this, menu]()
{
menu->setFixedWidth(ui->m_addButton->width());
}
);
ui->m_addButton->setMenu(menu);
AZStd::string buttonText = "Add another ";
AzFramework::StringFunc::Join(buttonText, classNames.begin(), classNames.end(), " or ");
ui->m_addButton->setText(buttonText.c_str());
}
}
AZStd::string ManifestWidgetPage::ClassIdToName(const Uuid& id) const
{
static const AZStd::string s_groupSuffix = "group";
const SerializeContext::ClassData* classData = m_context->FindClassData(id);
if (!classData)
{
return "<type not registered>";
}
AZStd::string className;
if (classData->m_editData)
{
className = classData->m_editData->m_name;
}
else
{
className = classData->m_name;
}
// Get rid of "Group" suffix and all trailing whitespace (e.g. "mesh group" -> "mesh")
if (className.length() > s_groupSuffix.length())
{
size_t potentialSuffixOffset = className.length() - s_groupSuffix.length();
if (AzFramework::StringFunc::Equal(className.c_str() + potentialSuffixOffset, s_groupSuffix.c_str()))
{
AzFramework::StringFunc::LKeep(className, potentialSuffixOffset - 1);
AzFramework::StringFunc::Strip(className, ' ');
}
}
return className;
}
void ManifestWidgetPage::AddNewObject(const Uuid& id)
{
AZ_TraceContext("Instance id", id);
const SerializeContext::ClassData* classData = m_context->FindClassData(id);
AZ_Assert(classData, "Type not registered.");
if (classData)
{
AZ_TraceContext("Object Type", classData->m_name);
AZ_Assert(classData->m_factory, "Registered type has no factory to create a new instance with.");
if (classData->m_factory)
{
ManifestWidget* parent = ManifestWidget::FindRoot(this);
AZ_Assert(parent, "ManifestWidgetPage isn't docked in a ManifestWidget.");
if (!parent)
{
return;
}
AZStd::shared_ptr<Containers::Scene> scene = parent->GetScene();
if (!scene)
{
return;
}
Containers::SceneManifest& manifest = scene->GetManifest();
void* rawInstance = classData->m_factory->Create(classData->m_name);
AZ_Assert(rawInstance, "Serialization factory failed to construct new instance.");
if (!rawInstance)
{
return;
}
AZStd::shared_ptr<DataTypes::IManifestObject> instance(reinterpret_cast<DataTypes::IManifestObject*>(rawInstance));
EBUS_EVENT(Events::ManifestMetaInfoBus, InitializeObject, *scene, *instance);
if (!manifest.AddEntry(instance))
{
AZ_Assert(false, "Unable to add new object to manifest.");
}
if (!AddObject(instance))
{
AZ_Assert(false, "Unable to add new object to Reflected Property Editor.");
}
// Refresh the page after adding this new object.
RefreshPage();
EmitObjectChanged();
}
}
}
void ManifestWidgetPage::EmitObjectChanged(const DataTypes::IManifestObject* object)
{
ManifestWidget* parent = ManifestWidget::FindRoot(this);
AZ_Assert(parent, "ManifestWidgetPage isn't docked in a ManifestWidget.");
if (!parent)
{
return;
}
AZStd::shared_ptr<Containers::Scene> scene = parent->GetScene();
if (!scene)
{
return;
}
Events::ManifestMetaInfoBus::Broadcast(&Events::ManifestMetaInfoBus::Events::ObjectUpdated, *scene, object, this);
}
void ManifestWidgetPage::ObjectUpdated([[maybe_unused]] const Containers::Scene& scene, const DataTypes::IManifestObject* target, void* sender)
{
if (sender != this && target != nullptr && m_propertyEditor)
{
if (AZStd::find_if(m_objects.begin(), m_objects.end(),
[target](const AZStd::shared_ptr<DataTypes::IManifestObject>& object)
{
return object.get() == target;
}) != m_objects.end())
{
m_propertyEditor->InvalidateAttributesAndValues();
}
}
}
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
#include <SceneWidgets/moc_ManifestWidgetPage.cpp>
@@ -0,0 +1,104 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Math/Uuid.h>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
#include <SceneAPI/SceneCore/Events/ManifestMetaInfoBus.h>
#endif
namespace AZ
{
class SerializeContext;
namespace SceneAPI
{
namespace DataTypes
{
class IManifestObject;
}
namespace UI
{
// QT space
namespace Ui
{
class ManifestWidgetPage;
}
class ManifestWidgetPage
: public QWidget
, public AzToolsFramework::IPropertyEditorNotify
, public Events::ManifestMetaInfoBus::Handler
{
Q_OBJECT
public:
ManifestWidgetPage(SerializeContext* context, AZStd::vector<AZ::Uuid>&& classTypeIds);
~ManifestWidgetPage() override;
// Sets the number of entries the user can add through this widget. It doesn't limit
// the amount of entries that can be stored.
virtual void SetCapSize(size_t size);
virtual size_t GetCapSize() const;
virtual bool SupportsType(const AZStd::shared_ptr<DataTypes::IManifestObject>& object);
virtual bool AddObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object);
virtual bool RemoveObject(const AZStd::shared_ptr<DataTypes::IManifestObject>& object);
virtual size_t ObjectCount() const;
virtual void Clear();
virtual void ScrollToBottom();
void RefreshPage(); // Called when a scene is initially loaded, after all objects are populated.
protected slots:
//! Callback that's triggered when the add button only has 1 entry.
void OnSingleGroupAdd();
protected:
//! Callback that's triggered when the add button has multiple entries.
virtual void OnMultiGroupAdd(const Uuid& id);
virtual void BuildAndConnectAddButton();
virtual AZStd::string ClassIdToName(const Uuid& id) const;
virtual void AddNewObject(const Uuid& id);
//! Report that an object on this page has been updated.
//! @param object Pointer to the changed object. If the manifest itself has been update
//! for instance after adding or removing a group use null to update the entire manifest.
virtual void EmitObjectChanged(const DataTypes::IManifestObject* object = nullptr);
// IPropertyEditorNotify Interface Methods
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void SetPropertyEditingActive(AzToolsFramework::InstanceDataNode* pNode) override;
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
void SealUndoStack() override;
// ManifestMetaInfoBus
void ObjectUpdated(const Containers::Scene& scene, const DataTypes::IManifestObject* target, void* sender) override;
AZStd::vector<AZ::Uuid> m_classTypeIds;
AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject>> m_objects;
QScopedPointer<Ui::ManifestWidgetPage> ui;
AzToolsFramework::ReflectedPropertyEditor* m_propertyEditor;
SerializeContext* m_context;
size_t m_capSize;
};
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::UI::ManifestWidgetPage</class>
<widget class="QWidget" name="AZ::SceneAPI::UI::ManifestWidgetPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>275</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="header">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<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="QPushButton" name="m_addButton">
<property name="maximumSize">
<size>
<width>250</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Add another</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="m_mainLayout"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,107 @@
/*
* 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 <QSplitter>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <SceneWidgets/ui_SceneGraphInspectWidget.h>
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
#include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h>
#include <SceneAPI/SceneUI/SceneWidgets/SceneGraphWidget.h>
#include <SceneAPI/SceneUI/SceneWidgets/SceneGraphInspectWidget.h>
#include <AzToolsFramework/UI/PropertyEditor/ReflectedPropertyEditor.hxx>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(SceneGraphInspectWidget, SystemAllocator, 0);
SceneGraphInspectWidget::SceneGraphInspectWidget(const Containers::Scene& scene, QWidget* parent, SerializeContext* context)
: QWidget(parent)
, ui(new Ui::SceneGraphInspectWidget())
, m_graphView(aznew SceneGraphWidget(scene, this))
, m_propertyEditor(aznew AzToolsFramework::ReflectedPropertyEditor(this))
, m_context(context)
{
ui->setupUi(this);
if (!m_context)
{
ComponentApplicationBus::BroadcastResult(m_context, &ComponentApplicationBus::Events::GetSerializeContext);
}
m_propertyEditor->Setup(m_context, nullptr, true, 100);
m_propertyEditor->setEnabled(false);
m_graphView->Build();
ui->m_splitter->insertWidget(0, m_graphView.data());
ui->m_propertyEditorLayout->addWidget(m_propertyEditor.data());
connect(m_graphView.data(), &SceneGraphWidget::SelectionChanged, this, &SceneGraphInspectWidget::OnSelectionChanged);
}
SceneGraphInspectWidget::~SceneGraphInspectWidget() = default;
void SceneGraphInspectWidget::OnSelectionChanged(AZStd::shared_ptr<const DataTypes::IGraphObject> item)
{
using namespace AZ::SceneAPI::Events;
if (item)
{
if (m_context)
{
// Only try to show if there's a registered editor for the class.
const SerializeContext::ClassData* classData = m_context->FindClassData(item->RTTI_GetType());
if (classData && classData->m_editData)
{
// The reflected property editor is made for editing (as the name suggest) not inspecting,
// therefore it only accepts objects it can modify.
DataTypes::IGraphObject* mutableItem = const_cast<DataTypes::IGraphObject*>(item.get());
m_propertyEditor->ClearInstances();
m_propertyEditor->AddInstance(mutableItem, item->RTTI_GetType());
m_propertyEditor->InvalidateAll();
m_propertyEditor->ExpandAll();
ui->m_infoStack->setCurrentIndex(1);
return;
}
}
AZStd::string description = "<html><head/><body><p>";
if (item->RTTI_GetTypeName())
{
description += "<b>";
description += item->RTTI_GetTypeName();
description += "</b></p><p>";
}
AZStd::string tooltip;
GraphMetaInfoBus::Broadcast(&GraphMetaInfoBus::Events::GetToolTip, tooltip, item.get());
description += tooltip.empty() ? "No information found for this node." : tooltip;
description += "</p></body></html>";
ui->m_noSelectionLabel->setText(description.c_str());
}
else
{
ui->m_noSelectionLabel->setText("Empty node selected.");
}
ui->m_infoStack->setCurrentIndex(0);
}
} // UI
} // SceneAPI
} // AZ
#include <SceneWidgets/moc_SceneGraphInspectWidget.cpp>
@@ -0,0 +1,76 @@
#pragma once
/*
* 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.
*
*/
#if !defined(Q_MOC_RUN)
#include <QWidget>
#include <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
namespace AzToolsFramework
{
class ReflectedPropertyEditor;
}
namespace AZ
{
class SerializeContext;
namespace SceneAPI
{
namespace Containers
{
class Scene;
}
namespace DataTypes
{
class IGraphObject;
}
namespace UI
{
// QT space
namespace Ui
{
class SceneGraphInspectWidget;
}
class SceneGraphWidget;
class SCENE_UI_API SceneGraphInspectWidget
: public QWidget
{
public:
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL;
explicit SceneGraphInspectWidget(const Containers::Scene& scene, QWidget* parent = nullptr, SerializeContext* context = nullptr);
~SceneGraphInspectWidget() override;
protected:
void OnSelectionChanged(AZStd::shared_ptr<const DataTypes::IGraphObject> item);
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
QScopedPointer<Ui::SceneGraphInspectWidget> ui;
QScopedPointer<SceneGraphWidget> m_graphView;
QScopedPointer<AzToolsFramework::ReflectedPropertyEditor> m_propertyEditor;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
SerializeContext* m_context;
};
} // UI
} // SceneAPI
} // AZ
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::UI::SceneGraphInspectWidget</class>
<widget class="QWidget" name="AZ::SceneAPI::UI::SceneGraphInspectWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>314</width>
<height>275</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</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="QSplitter" name="m_splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QStackedWidget" name="m_infoStack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="m_noSelection">
<layout class="QVBoxLayout" name="noSelectionLayout">
<item>
<widget class="QLabel" name="m_noSelectionLabel">
<property name="text">
<string>Select a node to inspect its properties in a read-only format.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::Alignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop)</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="m_propertyEditor">
<layout class="QHBoxLayout" name="m_propertyEditorLayout"/>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,538 @@
/*
* 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 <QStandardItemModel>
#include <SceneWidgets/ui_SceneGraphWidget.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/stack.h>
#include <AzToolsFramework/Debug/TraceContext.h>
#include <SceneAPI/SceneCore/Containers/Scene.h>
#include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
#include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
#include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
#include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
#include <SceneAPI/SceneCore/Events/GraphMetaInfoBus.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneUI/SceneWidgets/SceneGraphWidget.h>
namespace AZ
{
namespace SceneAPI
{
namespace UI
{
AZ_CLASS_ALLOCATOR_IMPL(SceneGraphWidget, SystemAllocator, 0)
SceneGraphWidget::SceneGraphWidget(const Containers::Scene& scene, QWidget* parent)
: QWidget(parent)
, ui(new Ui::SceneGraphWidget())
, m_treeModel(new QStandardItemModel())
, m_scene(scene)
, m_targetList(nullptr)
, m_selectedCount(0)
, m_totalCount(0)
, m_endPointOption(EndPointOption::AlwaysShow)
, m_checkableOption(CheckableOption::NoneCheckable)
{
SetupUI();
}
SceneGraphWidget::SceneGraphWidget(const Containers::Scene& scene, const DataTypes::ISceneNodeSelectionList& targetList,
QWidget* parent)
: QWidget(parent)
, ui(new Ui::SceneGraphWidget())
, m_treeModel(new QStandardItemModel())
, m_scene(scene)
, m_targetList(targetList.Copy())
, m_selectedCount(0)
, m_totalCount(0)
, m_endPointOption(EndPointOption::OnlyShowFilterTypes)
, m_checkableOption(CheckableOption::AllCheckable)
{
SetupUI();
}
SceneGraphWidget::~SceneGraphWidget() = default;
AZStd::unique_ptr<DataTypes::ISceneNodeSelectionList>&& SceneGraphWidget::ClaimTargetList()
{
return AZStd::move(m_targetList);
}
void SceneGraphWidget::IncludeEndPoints(EndPointOption option)
{
m_endPointOption = option;
}
void SceneGraphWidget::MakeCheckable(CheckableOption option)
{
m_checkableOption = option;
}
void SceneGraphWidget::AddFilterType(const Uuid& id)
{
if (m_filterTypes.find(id) == m_filterTypes.end())
{
m_filterTypes.insert(id);
}
}
void SceneGraphWidget::AddVirtualFilterType(Crc32 name)
{
if (m_filterVirtualTypes.find(name) == m_filterVirtualTypes.end())
{
m_filterVirtualTypes.insert(name);
}
}
void SceneGraphWidget::SetupUI()
{
ui->setupUi(this);
ui->m_selectionTree->setHeaderHidden(true);
ui->m_selectionTree->setModel(m_treeModel.data());
connect(ui->m_selectAllCheckBox, &QCheckBox::stateChanged, this, &SceneGraphWidget::OnSelectAllCheckboxStateChanged);
connect(m_treeModel.data(), &QStandardItemModel::itemChanged, this, &SceneGraphWidget::OnTreeItemStateChanged);
connect(ui->m_selectionTree->selectionModel(), &QItemSelectionModel::currentChanged, this, &SceneGraphWidget::OnTreeItemChanged);
}
void SceneGraphWidget::Build()
{
setUpdatesEnabled(false);
QSignalBlocker blocker(m_treeModel.data());
const Containers::SceneGraph& graph = m_scene.GetGraph();
m_selectedCount = 0;
m_totalCount = 0;
m_treeModel->clear();
m_treeItems.clear();
m_treeItems = AZStd::vector<QStandardItem*>(graph.GetNodeCount(), nullptr);
if (m_checkableOption == CheckableOption::NoneCheckable)
{
ui->m_selectAllCheckBox->hide();
}
else
{
ui->m_selectAllCheckBox->show();
}
auto sceneGraphView = Containers::Views::MakePairView(graph.GetNameStorage(), graph.GetContentStorage());
auto sceneGraphDownardsIteratorView = Containers::Views::MakeSceneGraphDownwardsView<Containers::Views::BreadthFirst>(
graph, graph.GetRoot(), sceneGraphView.begin(), true);
// Some importer implementations may write an empty node to force collection all items under a common root
// If that is the case, we're going to skip it so we don't show the user an empty node root
auto iterator = sceneGraphDownardsIteratorView.begin();
if (iterator->first.GetPathLength() == 0 && !iterator->second)
{
++iterator;
}
for (; iterator != sceneGraphDownardsIteratorView.end(); ++iterator)
{
Containers::SceneGraph::HierarchyStorageConstIterator hierarchy = iterator.GetHierarchyIterator();
Containers::SceneGraph::NodeIndex currentIndex = graph.ConvertToNodeIndex(hierarchy);
AZ_Assert(currentIndex.IsValid(), "While iterating through the Scene Graph an unexpected invalid entry was found.");
AZStd::shared_ptr<const DataTypes::IGraphObject> currentItem = iterator->second;
if (hierarchy->IsEndPoint())
{
switch (m_endPointOption)
{
case EndPointOption::AlwaysShow:
break;
case EndPointOption::NeverShow:
continue;
case EndPointOption::OnlyShowFilterTypes:
if (IsFilteredType(currentItem, currentIndex))
{
break;
}
else
{
continue;
}
default:
AZ_Assert(false, "Unsupported type %i for end point option.", m_endPointOption);
break;
}
}
bool isCheckable = false;
switch (m_checkableOption)
{
case CheckableOption::AllCheckable:
isCheckable = true;
break;
case CheckableOption::NoneCheckable:
isCheckable = false;
break;
case CheckableOption::OnlyFilterTypesCheckable:
isCheckable = IsFilteredType(currentItem, currentIndex);
break;
default:
AZ_Assert(false, "Unsupported type %i for checkable option.", m_checkableOption);
isCheckable = false;
break;
}
QStandardItem* treeItem = BuildTreeItem(currentItem, iterator->first, isCheckable, hierarchy->IsEndPoint());
if (isCheckable)
{
if (IsSelected(iterator->first, false))
{
treeItem->setCheckState(Qt::CheckState::Checked);
m_selectedCount++;
}
m_totalCount++;
}
m_treeItems[currentIndex.AsNumber()] = treeItem;
Containers::SceneGraph::NodeIndex parentIndex = graph.GetNodeParent(currentIndex);
if (parentIndex.IsValid() && m_treeItems[parentIndex.AsNumber()])
{
m_treeItems[parentIndex.AsNumber()]->appendRow(treeItem);
}
else
{
m_treeModel->appendRow(treeItem);
}
}
ui->m_selectionTree->expandAll();
UpdateSelectAllStatus();
setUpdatesEnabled(true);
}
bool SceneGraphWidget::IsFilteredType(const AZStd::shared_ptr<const DataTypes::IGraphObject>& object,
Containers::SceneGraph::NodeIndex index) const
{
if (!object)
{
return false;
}
for (const Uuid& id : m_filterTypes)
{
if (object->RTTI_IsTypeOf(id))
{
return true;
}
}
if (!m_filterVirtualTypes.empty())
{
AZStd::set<Crc32> virtualTypes;
EBUS_EVENT(Events::GraphMetaInfoBus, GetVirtualTypes, virtualTypes, m_scene, index);
for (Crc32 name : virtualTypes)
{
if (m_filterVirtualTypes.find(name) != m_filterVirtualTypes.end())
{
return true;
}
}
}
return false;
}
QStandardItem* SceneGraphWidget::BuildTreeItem(const AZStd::shared_ptr<const DataTypes::IGraphObject>& object,
const Containers::SceneGraph::Name& name, bool isCheckable, [[maybe_unused]] bool isEndPoint) const
{
QStandardItem* treeItem = new QStandardItem(name.GetName());
treeItem->setData(QString(name.GetPath()));
treeItem->setEditable(false);
treeItem->setCheckable(isCheckable);
if (object)
{
AZStd::string toolTip;
EBUS_EVENT(Events::GraphMetaInfoBus, GetToolTip, toolTip, object.get());
if (toolTip.empty())
{
treeItem->setToolTip(QString::asprintf("%s\n<%s>", name.GetPath(), object->RTTI_GetTypeName()));
}
else
{
treeItem->setToolTip(QString::asprintf("%s\n\n%s", name.GetPath(), toolTip.c_str()));
}
AZStd::string iconPath;
EBUS_EVENT(Events::GraphMetaInfoBus, GetIconPath, iconPath, object.get());
if (!iconPath.empty())
{
treeItem->setIcon(QIcon(iconPath.c_str()));
}
}
return treeItem;
}
void SceneGraphWidget::OnSelectAllCheckboxStateChanged()
{
setUpdatesEnabled(false);
QSignalBlocker blocker(m_treeModel.data());
Qt::CheckState state = ui->m_selectAllCheckBox->checkState();
if (m_targetList)
{
m_targetList->ClearSelectedNodes();
m_targetList->ClearUnselectedNodes();
for (QStandardItem* item : m_treeItems)
{
if (!item || !item->isCheckable())
{
continue;
}
item->setCheckState(state);
QVariant itemData = item->data();
if (itemData.isValid())
{
AZStd::string fullName = itemData.toString().toUtf8().data();
if (state == Qt::CheckState::Unchecked)
{
m_targetList->RemoveSelectedNode(fullName);
}
else
{
m_targetList->AddSelectedNode(AZStd::move(fullName));
}
}
}
}
else
{
for (QStandardItem* item : m_treeItems)
{
if (item && item->isCheckable())
{
item->setCheckState(state);
}
}
}
m_selectedCount = (state == Qt::CheckState::Unchecked) ? 0 : m_totalCount;
UpdateSelectAllStatus();
setUpdatesEnabled(true);
}
void SceneGraphWidget::OnTreeItemStateChanged(QStandardItem* item)
{
setUpdatesEnabled(false);
QSignalBlocker blocker(m_treeModel.data());
Qt::CheckState state = item->checkState();
bool decrement = (state == Qt::CheckState::Unchecked);
if (decrement)
{
if (!RemoveSelection(item))
{
item->setCheckState(Qt::CheckState::Checked);
return;
}
}
else
{
if (!AddSelection(item))
{
item->setCheckState(Qt::CheckState::Unchecked);
return;
}
}
AZStd::stack<QStandardItem*> children;
int rowCount = item->rowCount();
for (int index = 0; index < rowCount; ++index)
{
children.push(item->child(index));
}
while (!children.empty())
{
QStandardItem* current = children.top();
children.pop();
if (decrement)
{
if (current->checkState() != Qt::CheckState::Unchecked && RemoveSelection(current))
{
current->setCheckState(state);
}
}
else
{
if (current->checkState() == Qt::CheckState::Unchecked && AddSelection(current))
{
current->setCheckState(state);
}
}
int rowCount2 = current->rowCount();
for (int index = 0; index < rowCount2; ++index)
{
children.push(current->child(index));
}
}
UpdateSelectAllStatus();
setUpdatesEnabled(true);
}
void SceneGraphWidget::OnTreeItemChanged(const QModelIndex& current, const QModelIndex& /*previous*/)
{
QStandardItem* item = m_treeModel->itemFromIndex(current);
QVariant itemData = item->data();
if (!itemData.isValid() || itemData.type() != QVariant::Type::String)
{
return;
}
AZStd::string fullName = itemData.toString().toUtf8().data();
AZ_TraceContext("Selected item", fullName);
Containers::SceneGraph::NodeIndex nodeIndex = m_scene.GetGraph().Find(fullName);
AZ_Assert(nodeIndex.IsValid(), "Invalid node added to tree.");
if (!nodeIndex.IsValid())
{
return;
}
Q_EMIT SelectionChanged(m_scene.GetGraph().GetNodeContent(nodeIndex));
}
void SceneGraphWidget::UpdateSelectAllStatus()
{
QSignalBlocker blocker(ui->m_selectAllCheckBox);
if (m_selectedCount == m_totalCount)
{
ui->m_selectAllCheckBox->setText("Unselect all");
ui->m_selectAllCheckBox->setCheckState(Qt::CheckState::Checked);
}
else
{
ui->m_selectAllCheckBox->setText("Select all");
ui->m_selectAllCheckBox->setCheckState(Qt::CheckState::Unchecked);
}
}
bool SceneGraphWidget::IsSelected(const Containers::SceneGraph::Name& name, bool updateNodeSelection) const
{
if (!m_targetList)
{
return false;
}
if (updateNodeSelection)
{
// Use a temp list to get a valid state of the UI here based on selected/unselected nodes
// We use the temp list so that the real list actually keeps track of the user's selection
// Since UpdateNodeSelection will modify selected/unselected node lists for us.
AZStd::unique_ptr<DataTypes::ISceneNodeSelectionList> tempList(m_targetList->Copy());
Utilities::SceneGraphSelector::UpdateNodeSelection(m_scene.GetGraph(), *tempList);
return IsSelectedInSelectionList(name, *tempList);
}
else
{
return IsSelectedInSelectionList(name, *m_targetList);
}
}
bool SceneGraphWidget::IsSelectedInSelectionList(const Containers::SceneGraph::Name& name, const DataTypes::ISceneNodeSelectionList& targetList) const
{
size_t count = targetList.GetSelectedNodeCount();
for (size_t selectedNodeIndex = 0; selectedNodeIndex < count; ++selectedNodeIndex)
{
if (targetList.GetSelectedNode(selectedNodeIndex) == name.GetPath())
{
return true;
}
}
return false;
}
bool SceneGraphWidget::AddSelection(const QStandardItem* item)
{
if (!m_targetList)
{
return true;
}
QVariant itemData = item->data();
if (!itemData.isValid() || itemData.type() != QVariant::Type::String)
{
return false;
}
AZStd::string fullName = itemData.toString().toUtf8().data();
AZ_TraceContext("Item for addition", fullName);
Containers::SceneGraph::NodeIndex nodeIndex = m_scene.GetGraph().Find(fullName);
AZ_Assert(nodeIndex.IsValid(), "Invalid node added to tree.");
if (!nodeIndex.IsValid())
{
return false;
}
m_targetList->AddSelectedNode(fullName);
m_selectedCount++;
AZ_Assert(m_selectedCount <= m_totalCount, "Selected node count exceeds available node count.");
return true;
}
bool SceneGraphWidget::RemoveSelection(const QStandardItem* item)
{
if (!m_targetList)
{
return true;
}
QVariant itemData = item->data();
if (!itemData.isValid() || itemData.type() != QVariant::Type::String)
{
return false;
}
AZStd::string fullName = itemData.toString().toUtf8().data();
AZ_TraceContext("Item for removal", fullName);
Containers::SceneGraph::NodeIndex nodeIndex = m_scene.GetGraph().Find(fullName);
AZ_Assert(nodeIndex.IsValid(), "Invalid node removed from tree.");
if (!nodeIndex.IsValid())
{
return false;
}
m_targetList->RemoveSelectedNode(fullName);
AZ_Assert(m_selectedCount > 0, "Selected node count can not be decremented below zero.");
m_selectedCount--;
return true;
}
QCheckBox* SceneGraphWidget::GetQCheckBox()
{
return ui->m_selectAllCheckBox;
}
QTreeView* SceneGraphWidget::GetQTreeView()
{
return ui->m_selectionTree;
}
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
#include <SceneWidgets/moc_SceneGraphWidget.cpp>
@@ -0,0 +1,146 @@
/*
* 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 <string>
#include <QWidget>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <SceneAPI/SceneCore/Containers/SceneGraph.h>
#include <SceneAPI/SceneUI/SceneUIConfiguration.h>
#endif
class QStandardItem;
class QStandardItemModel;
class QCheckBox;
class QTreeView;
namespace AZ
{
namespace SceneAPI
{
namespace Containers
{
class Scene;
}
namespace DataTypes
{
class IGraphObject;
class ISceneNodeSelectionList;
}
namespace UI
{
// QT space
namespace Ui
{
class SceneGraphWidget;
}
class SCENE_UI_API SceneGraphWidget
: public QWidget
{
public:
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR_DECL
// Sets default settings for the widget. Content will not be constructed until "Build" is called.
SceneGraphWidget(const Containers::Scene& scene, QWidget* parent = nullptr);
// Sets default settings for the widget. Content will not be constructed until "Build" is called.
SceneGraphWidget(const Containers::Scene& scene, const DataTypes::ISceneNodeSelectionList& targetList,
QWidget* parent = nullptr);
~SceneGraphWidget() override;
AZStd::unique_ptr<DataTypes::ISceneNodeSelectionList>&& ClaimTargetList();
enum class EndPointOption
{
AlwaysShow, // End points are always shown.
NeverShow, // End points are never shown.
OnlyShowFilterTypes // End points are only shown if its type is in the filter type list.
};
// Updates the tree to include/exclude end points. Call "BuildTree()" to rebuild the tree.
virtual void IncludeEndPoints(EndPointOption option);
enum class CheckableOption
{
AllCheckable, // All nodes in the tree can be checked.
NoneCheckable, // No nodes can be checked.
OnlyFilterTypesCheckable // Only nodes in the filter type list can be checked.
};
// Updates the tree to include/exclude check boxes and the master selection. Call "BuildTree()" to rebuild the tree.
virtual void MakeCheckable(CheckableOption option);
// Add a type to filter for. Filter types are used to determine if a check box is added and/or to be shown if
// the type is an end point. See "IncludeEndPoints" and "MakeCheckable" for more details.
// Call "Build()" to rebuild the tree.
virtual void AddFilterType(const Uuid& id);
// Add a virtual type to filter for. Filter types are used to determine if a check box is added and/or to be shown if
// the type is an end point. See "IncludeEndPoints" and "MakeCheckable" for more details.
// Call "Build()" to rebuild the tree.
virtual void AddVirtualFilterType(Crc32 name);
// Constructs the widget's content. Call this after making one or more changes to the settings.
virtual void Build();
Q_SIGNALS:
void SelectionChanged(AZStd::shared_ptr<const DataTypes::IGraphObject> item);
protected:
virtual void SetupUI();
virtual bool IsFilteredType(const AZStd::shared_ptr<const DataTypes::IGraphObject>& object,
Containers::SceneGraph::NodeIndex index) const;
virtual QStandardItem* BuildTreeItem(const AZStd::shared_ptr<const DataTypes::IGraphObject>& object,
const Containers::SceneGraph::Name& name, bool isCheckable, bool isEndPoint) const;
virtual void OnSelectAllCheckboxStateChanged();
virtual void OnTreeItemStateChanged(QStandardItem* item);
virtual void OnTreeItemChanged(const QModelIndex& current, const QModelIndex& previous);
virtual void UpdateSelectAllStatus();
/// If you are calling this on a lot of elements in quick succession (like in a Build function), set
/// updateNodeSelection to false for increased performance.
virtual bool IsSelected(const Containers::SceneGraph::Name& name, bool updateNodeSelection = true) const;
virtual bool AddSelection(const QStandardItem* item);
virtual bool RemoveSelection(const QStandardItem* item);
QCheckBox* GetQCheckBox();
QTreeView* GetQTreeView();
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
AZStd::vector<QStandardItem*> m_treeItems;
AZStd::set<Uuid> m_filterTypes;
AZStd::set<Crc32> m_filterVirtualTypes;
QScopedPointer<Ui::SceneGraphWidget> ui;
QScopedPointer<QStandardItemModel> m_treeModel;
AZStd::unique_ptr<DataTypes::ISceneNodeSelectionList> m_targetList;
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
const Containers::Scene& m_scene;
size_t m_selectedCount;
size_t m_totalCount;
EndPointOption m_endPointOption;
CheckableOption m_checkableOption;
private:
bool IsSelectedInSelectionList(const Containers::SceneGraph::Name& name, const DataTypes::ISceneNodeSelectionList& targetList) const;
};
} // namespace UI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AZ::SceneAPI::UI::SceneGraphWidget</class>
<widget class="QWidget" name="AZ::SceneAPI::UI::SceneGraphWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>258</width>
<height>222</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<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>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="m_selectAllCheckBox">
<property name="text">
<string>Select all nodes</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="m_selectionTree"/>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,134 @@
/*
* 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 <AzTest/AzTest.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Math/Quaternion.h>
#include <SceneAPI/SceneUI/RowWidgets/TransformRowWidget.h>
namespace AZ
{
namespace SceneAPI
{
namespace SceneUI
{
class TransformRowWidgetTest
: public ::testing::Test
{
public:
ExpandedTransform m_expanded;
Transform m_transform;
Vector3 m_translation = Vector3(10.0f, 20.0f, 30.0f);
Vector3 m_rotation = Vector3(30.0f, 45.0f, 60.0f);
Vector3 m_scale = Vector3(2.0f, 3.0f, 4.0f);
};
TEST_F(TransformRowWidgetTest, GetTranslation_TranslationInMatrix_TranslationCanBeRetrievedDirectly)
{
m_transform = Transform::CreateTranslation(m_translation);
m_expanded.SetTransform(m_transform);
const Vector3& returned = m_expanded.GetTranslation();
EXPECT_NEAR(m_translation.GetX(), returned.GetX(), 0.1f);
EXPECT_NEAR(m_translation.GetY(), returned.GetY(), 0.1f);
EXPECT_NEAR(m_translation.GetZ(), returned.GetZ(), 0.1f);
}
TEST_F(TransformRowWidgetTest, GetTranslation_TranslationInMatrix_TranslationCanBeRetrievedFromTransform)
{
m_transform = Transform::CreateTranslation(m_translation);
m_expanded.SetTransform(m_transform);
Transform rebuild;
m_expanded.GetTransform(rebuild);
Vector3 returned = rebuild.GetTranslation();
EXPECT_NEAR(m_translation.GetX(), returned.GetX(), 0.1f);
EXPECT_NEAR(m_translation.GetY(), returned.GetY(), 0.1f);
EXPECT_NEAR(m_translation.GetZ(), returned.GetZ(), 0.1f);
}
TEST_F(TransformRowWidgetTest, DISABLED_GetRotation_RotationInMatrix_RotationCanBeRetrievedDirectly)
{
m_transform = AZ::ConvertEulerDegreesToTransform(m_rotation);
m_expanded.SetTransform(m_transform);
const Vector3& returned = m_expanded.GetRotation();
EXPECT_NEAR(m_rotation.GetX(), returned.GetX(), 1.0f);
EXPECT_NEAR(m_rotation.GetY(), returned.GetY(), 1.0f);
EXPECT_NEAR(m_rotation.GetZ(), returned.GetZ(), 1.0f);
}
TEST_F(TransformRowWidgetTest, DISABLED_GetRotation_RotationInMatrix_RotationCanBeRetrievedFromTransform)
{
m_transform.SetFromEulerDegrees(m_rotation);
m_expanded.SetTransform(m_transform);
Transform rebuild;
m_expanded.GetTransform(rebuild);
Vector3 returned = rebuild.GetEulerDegrees();
EXPECT_NEAR(m_rotation.GetX(), returned.GetX(), 1.0f);
EXPECT_NEAR(m_rotation.GetY(), returned.GetY(), 1.0f);
EXPECT_NEAR(m_rotation.GetZ(), returned.GetZ(), 1.0f);
}
TEST_F(TransformRowWidgetTest, GetScale_ScaleInMatrix_ScaleCanBeRetrievedDirectly)
{
m_transform = Transform::CreateScale(m_scale);
m_expanded.SetTransform(m_transform);
const Vector3& returned = m_expanded.GetScale();
EXPECT_NEAR(m_scale.GetX(), returned.GetX(), 0.1f);
EXPECT_NEAR(m_scale.GetY(), returned.GetY(), 0.1f);
EXPECT_NEAR(m_scale.GetZ(), returned.GetZ(), 0.1f);
}
TEST_F(TransformRowWidgetTest, GetScale_ScaleInMatrix_ScaleCanBeRetrievedFromTransform)
{
m_transform = Transform::CreateScale(m_scale);
m_expanded.SetTransform(m_transform);
Transform rebuild;
m_expanded.GetTransform(rebuild);
Vector3 returned = rebuild.GetScale();
EXPECT_NEAR(m_scale.GetX(), returned.GetX(), 0.1f);
EXPECT_NEAR(m_scale.GetY(), returned.GetY(), 0.1f);
EXPECT_NEAR(m_scale.GetZ(), returned.GetZ(), 0.1f);
}
TEST_F(TransformRowWidgetTest, GetTransform_RotateAndTranslateInMatrix_ReconstructedTransformMatchesOriginal)
{
Quaternion quaternion = AZ::ConvertEulerDegreesToQuaternion(m_rotation);
m_transform = Transform::CreateFromQuaternionAndTranslation(quaternion, m_translation);
m_expanded.SetTransform(m_transform);
Transform rebuild;
m_expanded.GetTransform(rebuild);
EXPECT_TRUE(m_transform.IsClose(rebuild, 0.001f));
}
TEST_F(TransformRowWidgetTest, GetTransform_RotateTranslateAndScaleInMatrix_ReconstructedTransformMatchesOriginal)
{
Quaternion quaternion = AZ::ConvertEulerDegreesToQuaternion(m_rotation);
m_transform = Transform::CreateFromQuaternionAndTranslation(quaternion, m_translation);
m_transform.MultiplyByScale(m_scale);
m_expanded.SetTransform(m_transform);
Transform rebuild;
m_expanded.GetTransform(rebuild);
EXPECT_TRUE(m_transform.IsClose(rebuild, 0.001f));
}
} // namespace SceneUI
} // namespace SceneAPI
} // namespace AZ
@@ -0,0 +1,33 @@
/*
* 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 <AzTest/AzTest.h>
class SceneUITestEnvironment
: public AZ::Test::ITestEnvironment
{
public:
virtual ~SceneUITestEnvironment() {}
protected:
void SetupEnvironment() override
{
}
void TeardownEnvironment() override
{
}
};
AZ_UNIT_TEST_HOOK(new SceneUITestEnvironment);