Merge pull request #7744 from aws-lumberyard-dev/Atom/guthadam/atom_tools_document_inspector

Atom Tools: Saving tool settings, made inspector configurable, moved to ATF
This commit is contained in:
Guthrie Adams
2022-02-18 12:09:11 -06:00
committed by GitHub
33 changed files with 501 additions and 405 deletions
@@ -0,0 +1,72 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#endif
namespace AtomToolsFramework
{
//! This is a specialized inspector widget that populates itself by inspecting document reflected object info.
//! Each element of an AtomToolsDocument object info vector will be displayed in a collapsible RPE group in the inspector.
//! Property changes emitted from each RPE will be tracked and used to signal undo/redo events in the document.
class AtomToolsDocumentInspector
: public InspectorWidget
, public AtomToolsDocumentNotificationBus::Handler
, public AzToolsFramework::IPropertyEditorNotify
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(AtomToolsDocumentInspector, AZ::SystemAllocator, 0);
AtomToolsDocumentInspector(const AZ::Crc32& toolId, QWidget* parent = nullptr);
~AtomToolsDocumentInspector() override;
//! Set the ID of the document that will be used to populate the inspector
void SetDocumentId(const AZ::Uuid& documentId);
//! Set a prefix string for storing registry settings
void SetDocumentSettingsPrefix(const AZStd::string& prefix);
using NodeIndicatorFunction = AZStd::function<const char*(const AzToolsFramework::InstanceDataNode*)>;
//! Set a function that will be used to determine what, if any, icon should be displayed next to a property in the inspector
void SetIndicatorFunction(const NodeIndicatorFunction& indicatorFunction);
// InspectorRequestBus::Handler overrides...
void Reset() override;
private:
// AtomToolsDocumentNotificationBus::Handler implementation
void OnDocumentObjectInfoChanged(const AZ::Uuid& documentId, const DocumentObjectInfo& objectInfo, bool rebuilt) override;
// AzToolsFramework::IPropertyEditorNotify overrides...
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void AfterPropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
void SealUndoStack() override {}
void RequestPropertyContextMenu([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, const QPoint&) override {}
void PropertySelectionChanged([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, bool) override {}
const AZ::Crc32 m_toolId = {};
bool m_editInProgress = {};
AZ::Uuid m_documentId = AZ::Uuid::CreateNull();
NodeIndicatorFunction m_nodeIndicatorFunction;
AZStd::string m_documentSettingsPrefix = "/O3DE/AtomToolsFramework/AtomToolsDocumentInspector";
};
} // namespace AtomToolsFramework
@@ -9,8 +9,12 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <AzCore/Memory/SystemAllocator.h>
#include <AtomToolsFramework/Inspector/InspectorRequestBus.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/string.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QVBoxLayout>
@@ -36,8 +40,10 @@ namespace AtomToolsFramework
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(InspectorWidget, AZ::SystemAllocator, 0);
AZ_RTTI(AtomToolsFramework::InspectorWidget, "{D77A5F5F-0536-4249-916F-328B272E1AAB}");
static void Reflect(AZ::ReflectContext* context);
explicit InspectorWidget(QWidget* parent = nullptr);
InspectorWidget(QWidget* parent = nullptr);
~InspectorWidget() override;
// InspectorRequestBus::Handler overrides...
@@ -74,6 +80,8 @@ namespace AtomToolsFramework
void ExpandAll() override;
void CollapseAll() override;
void SetGroupSettingsPrefix(const AZStd::string& prefix);
protected:
virtual bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const;
virtual void OnGroupExpanded(const AZStd::string& groupName);
@@ -81,14 +89,15 @@ namespace AtomToolsFramework
virtual void OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event);
private:
QScopedPointer<Ui::InspectorWidget> m_ui;
struct GroupWidgetPair
{
InspectorGroupHeaderWidget* m_header;
QWidget* m_panel;
};
QScopedPointer<Ui::InspectorWidget> m_ui;
AZStd::string m_collapsedGroupSettingName;
AZStd::unordered_set<AZ::u32> m_collapsedGroups;
AZStd::unordered_map<AZStd::string, GroupWidgetPair> m_groups;
};
} // namespace AtomToolsFramework
@@ -11,14 +11,8 @@
#include <Atom/RPI.Reflect/Material/MaterialPropertyDescriptor.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertyValue.h>
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/any.h>
namespace AzToolsFramework
{
class InstanceDataNode;
}
namespace AtomToolsFramework
{
//! Convert an editor property stored in a AZStd::any into a material property value
@@ -52,14 +46,4 @@ namespace AtomToolsFramework
[[maybe_unused]] const AZ::Name& propertyId,
const AZ::RPI::MaterialTypeSourceData::PropertyDefinition& propertyDefinition,
AZ::RPI::MaterialPropertyValue& propertyValue);
//! Generate a file path that is relative to either the source asset root or the export path
//! @param exportPath absolute path of the file being saved
//! @param referencePath absolute path of a file that will be treated as an external reference
//! @param relativeToExportPath specifies if the path is relative to the source asset root or the export path
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath = false);
//! Traverse up the instance data node hierarchy to find the containing dynamic property object
const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode);
} // namespace AtomToolsFramework
@@ -9,9 +9,12 @@
#pragma once
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/PlatformDef.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/std/any.h>
#include <AzCore/std/containers/vector.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QFileInfo>
@@ -23,14 +26,6 @@ class QImage;
namespace AtomToolsFramework
{
template<typename T>
T GetSettingOrDefault(AZStd::string_view path, const T& defaultValue)
{
T result;
auto settingsRegistry = AZ::SettingsRegistry::Get();
return (settingsRegistry && settingsRegistry->Get(result, path)) ? result : defaultValue;
}
using LoadImageAsyncCallback = AZStd::function<void(const QImage&)>;
void LoadImageAsync(const AZStd::string& path, LoadImageAsyncCallback callback);
@@ -39,4 +34,64 @@ namespace AtomToolsFramework
QFileInfo GetUniqueFileInfo(const QString& initialPath);
QFileInfo GetDuplicationFileInfo(const QString& initialPath);
bool LaunchTool(const QString& baseName, const QStringList& arguments);
//! Generate a file path that is relative to either the source asset root or the export path
//! @param exportPath absolute path of the file being saved
//! @param referencePath absolute path of a file that will be treated as an external reference
//! @param relativeToExportPath specifies if the path is relative to the source asset root or the export path
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath = false);
//! Traverse up the instance data hierarchy to find a node containing the corresponding type
template<typename T>
const T* FindAncestorInstanceDataNodeByType(const AzToolsFramework::InstanceDataNode* pNode)
{
// Traverse up the hierarchy from the input node to search for an instance matching the specified type
for (const auto* currentNode = pNode; currentNode; currentNode = currentNode->GetParent())
{
const auto* context = currentNode->GetSerializeContext();
const auto* classData = currentNode->GetClassMetadata();
if (context && classData)
{
if (context->CanDowncast(classData->m_typeId, azrtti_typeid<T>(), classData->m_azRtti, nullptr))
{
return static_cast<const T*>(currentNode->FirstInstance());
}
}
}
return nullptr;
}
template<typename T>
T GetSettingsValue(AZStd::string_view path, const T& defaultValue = {})
{
T result;
auto settingsRegistry = AZ::SettingsRegistry::Get();
return (settingsRegistry && settingsRegistry->Get(result, path)) ? result : defaultValue;
}
template<typename T>
bool SetSettingsValue(AZStd::string_view path, const T& value)
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
return settingsRegistry && settingsRegistry->Set(path, value);
}
template<typename T>
T GetSettingsObject(AZStd::string_view path, const T& defaultValue = {})
{
T result;
auto settingsRegistry = AZ::SettingsRegistry::Get();
return (settingsRegistry && settingsRegistry->GetObject<T>(result, path)) ? result : defaultValue;
}
template<typename T>
bool SetSettingsObject(AZStd::string_view path, const T& value)
{
auto settingsRegistry = AZ::SettingsRegistry::Get();
return settingsRegistry && settingsRegistry->SetObject<T>(path, value);
}
bool SaveSettingsToFile(const AZ::IO::FixedMaxPath& savePath, const AZStd::vector<AZStd::string>& filters);
} // namespace AtomToolsFramework
@@ -29,6 +29,7 @@ namespace AtomToolsFramework
~AtomToolsMainWindow();
protected:
// AtomToolsMainWindowRequestBus::Handler overrides...
void ActivateWindow() override;
bool AddDockWidget(const AZStd::string& name, QWidget* widget, uint32_t area, uint32_t orientation) override;
void RemoveDockWidget(const AZStd::string& name) override;
@@ -70,9 +70,9 @@ namespace AtomToolsFramework
m_styleManager->initialize(this, engineRootPath);
const int updateIntervalWhenActive =
aznumeric_cast<int>(GetSettingOrDefault<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenActive", 1));
aznumeric_cast<int>(GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenActive", 1));
const int updateIntervalWhenNotActive =
aznumeric_cast<int>(GetSettingOrDefault<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenNotActive", 64));
aznumeric_cast<int>(GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenNotActive", 64));
m_timer.setInterval(updateIntervalWhenActive);
connect(&m_timer, &QTimer::timeout, this, [this]()
@@ -188,7 +188,7 @@ namespace AtomToolsFramework
Base::StartCommon(systemEntity);
const bool clearLogFile = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false);
const bool clearLogFile = GetSettingsValue("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false);
m_traceLogger.OpenLogFile(m_targetName + ".log", clearLogFile);
ConnectToAssetProcessor();
@@ -197,7 +197,7 @@ namespace AtomToolsFramework
AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(
&AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotifications::OnDatabaseInitialized);
const bool enableSourceControl = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/EnableSourceControl", true);
const bool enableSourceControl = GetSettingsValue("/O3DE/AtomToolsFramework/Application/EnableSourceControl", true);
AzToolsFramework::SourceControlConnectionRequestBus::Broadcast(
&AzToolsFramework::SourceControlConnectionRequests::EnableSourceControl, enableSourceControl);
@@ -238,6 +238,12 @@ namespace AtomToolsFramework
void AtomToolsApplication::Destroy()
{
m_styleManager.reset();
// Save application settings to settings registry file
AZ::IO::FixedMaxPath savePath = AZ::Utils::GetProjectPath();
savePath /= AZStd::string::format("user/Registry/%s.setreg", m_targetName.c_str());
SaveSettingsToFile(savePath, { "/O3DE/AtomToolsFramework", AZStd::string::format("/O3DE/Atom/%s", m_targetName.c_str()) });
UnloadSettings();
AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
@@ -106,9 +106,9 @@ namespace AtomToolsFramework
const AZStd::vector<AssetBrowserEntry*> entries = m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets();
const bool promptToOpenMultipleFiles =
GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFiles", true);
GetSettingsValue<bool>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFiles", true);
const AZ::u64 promptToOpenMultipleFilesThreshold =
GetSettingOrDefault<AZ::u64>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFilesThreshold", 10);
GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/AssetBrowser/PromptToOpenMultipleFilesThreshold", 10);
if (promptToOpenMultipleFiles && promptToOpenMultipleFilesThreshold <= entries.size())
{
@@ -71,11 +71,11 @@ namespace AtomToolsFramework
QListWidgetItem* AssetGridDialog::CreateListItem(const SelectableAsset& selectableAsset)
{
const int itemBorder = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/ItemBorder", 4));
AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/AssetGridDialog/ItemBorder", 4));
const int itemSpacing = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/ItemSpacing", 10));
AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/AssetGridDialog/ItemSpacing", 10));
const int headerHeight = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/AtomToolsFramework/AssetGridDialog/HeaderHeight", 15));
AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/AtomToolsFramework/AssetGridDialog/HeaderHeight", 15));
const QSize gridSize = m_ui->m_assetList->gridSize();
m_ui->m_assetList->setGridSize(QSize(
@@ -13,6 +13,7 @@
#include <AtomToolsFramework/Document/AtomToolsDocument.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentSystem.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AtomToolsFrameworkSystemComponent.h>
namespace AtomToolsFramework
@@ -23,6 +24,7 @@ namespace AtomToolsFramework
DynamicPropertyGroup::Reflect(context);
AtomToolsDocument::Reflect(context);
AtomToolsDocumentSystem::Reflect(context);
InspectorWidget::Reflect(context);
if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
@@ -0,0 +1,120 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AtomToolsFramework/Document/AtomToolsDocumentInspector.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
namespace AtomToolsFramework
{
AtomToolsDocumentInspector::AtomToolsDocumentInspector(const AZ::Crc32& toolId, QWidget* parent)
: InspectorWidget(parent)
, m_toolId(toolId)
{
AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
}
AtomToolsDocumentInspector::~AtomToolsDocumentInspector()
{
AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
}
void AtomToolsDocumentInspector::SetDocumentId(const AZ::Uuid& documentId)
{
AddGroupsBegin();
m_documentId = documentId;
AtomToolsDocumentRequestBus::Event(
m_documentId,
[&](AtomToolsDocumentRequests* documentRequests)
{
if (documentRequests->IsOpen())
{
// Create a unique settings prefix string per document using a CRC of the document path
const AZStd::string groupSettingsPrefix = m_documentSettingsPrefix +
AZStd::string::format("/%08x/GroupSettings", aznumeric_cast<AZ::u32>(AZ::Crc32(documentRequests->GetAbsolutePath())));
SetGroupSettingsPrefix(groupSettingsPrefix);
// This will automatically expose all document contents to an inspector with a collapsible group per object.
// In the case of the material editor, this will be one inspector group per property group.
for (auto& objectInfo : documentRequests->GetObjectInfo())
{
// Passing in same main and comparison instance to enable custom value comparison
const AZ::Crc32 groupSaveStateKey(
AZStd::string::format("%s/%s", groupSettingsPrefix.c_str(), objectInfo.m_name.c_str()));
auto propertyGroupWidget = new InspectorPropertyGroupWidget(
objectInfo.m_objectPtr, objectInfo.m_objectPtr, objectInfo.m_objectType, this, this, groupSaveStateKey, {},
m_nodeIndicatorFunction, 0);
AddGroup(objectInfo.m_name, objectInfo.m_displayName, objectInfo.m_description, propertyGroupWidget);
SetGroupVisible(objectInfo.m_name, objectInfo.m_visible);
}
}
InspectorRequestBus::Handler::BusConnect(m_documentId);
});
AddGroupsEnd();
}
void AtomToolsDocumentInspector::SetDocumentSettingsPrefix(const AZStd::string& prefix)
{
m_documentSettingsPrefix = prefix;
}
void AtomToolsDocumentInspector::SetIndicatorFunction(const NodeIndicatorFunction& indicatorFunction)
{
m_nodeIndicatorFunction = indicatorFunction;
}
void AtomToolsDocumentInspector::Reset()
{
m_documentId = AZ::Uuid::CreateNull();
m_editInProgress = false;
InspectorWidget::Reset();
}
void AtomToolsDocumentInspector::OnDocumentObjectInfoChanged(
[[maybe_unused]] const AZ::Uuid& documentId, const DocumentObjectInfo& objectInfo, bool rebuilt)
{
if (m_documentId == documentId)
{
SetGroupVisible(objectInfo.m_name, objectInfo.m_visible);
if (rebuilt)
{
RebuildGroup(objectInfo.m_name);
}
else
{
RefreshGroup(objectInfo.m_name);
}
}
}
void AtomToolsDocumentInspector::BeforePropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode)
{
if (!m_editInProgress)
{
m_editInProgress = true;
AtomToolsDocumentRequestBus::Event(m_documentId, &AtomToolsDocumentRequestBus::Events::BeginEdit);
}
}
void AtomToolsDocumentInspector::SetPropertyEditingComplete([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode)
{
if (m_editInProgress)
{
m_editInProgress = false;
AtomToolsDocumentRequestBus::Event(m_documentId, &AtomToolsDocumentRequestBus::Events::EndEdit);
}
}
} // namespace AtomToolsFramework
//#include <AtomToolsFramework/Document/moc_AtomToolsDocumentInspector.cpp>
@@ -500,4 +500,4 @@ namespace AtomToolsFramework
}
} // namespace AtomToolsFramework
//#include <Document/moc_AtomToolsDocumentMainWindow.cpp>
//#include <AtomToolsFramework/Document/moc_AtomToolsDocumentMainWindow.cpp>
@@ -368,7 +368,7 @@ namespace AtomToolsFramework
void AtomToolsDocumentSystem::ReopenDocuments()
{
const bool enableHotReload = GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReload", true);
const bool enableHotReload = GetSettingsValue<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReload", true);
if (!enableHotReload)
{
m_documentIdsWithDependencyChanges.clear();
@@ -377,7 +377,7 @@ namespace AtomToolsFramework
}
const bool enableHotReloadPrompts =
GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReloadPrompts", true);
GetSettingsValue<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReloadPrompts", true);
for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges)
{
@@ -6,27 +6,69 @@
*
*/
#include <AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h>
#include <AtomToolsFramework/Inspector/InspectorGroupWidget.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Inspector/ui_InspectorWidget.h>
#include <QMenu>
#include <QScrollArea>
#include <QScrollBar>
#include <QSizePolicy>
#include <AtomToolsFramework/Inspector/InspectorGroupHeaderWidget.h>
#include <AtomToolsFramework/Inspector/InspectorGroupWidget.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <Inspector/ui_InspectorWidget.h>
namespace AtomToolsFramework
{
void InspectorWidget::Reflect(AZ::ReflectContext* context)
{
if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->RegisterGenericType<AZStd::unordered_set<AZ::u32>>();
serialize->Class<InspectorWidget>()
->Version(0);
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<InspectorRequestBus>("InspectorRequestBus")
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
->Attribute(AZ::Script::Attributes::Category, "Editor")
->Attribute(AZ::Script::Attributes::Module, "atomtools")
->Event("ClearHeading", &InspectorRequestBus::Events::ClearHeading)
->Event("Reset", &InspectorRequestBus::Events::Reset)
->Event("AddGroupsBegin", &InspectorRequestBus::Events::AddGroupsBegin)
->Event("AddGroupsEnd", &InspectorRequestBus::Events::AddGroupsEnd)
->Event("SetGroupVisible", &InspectorRequestBus::Events::SetGroupVisible)
->Event("IsGroupVisible", &InspectorRequestBus::Events::IsGroupVisible)
->Event("IsGroupHidden", &InspectorRequestBus::Events::IsGroupHidden)
->Event("RefreshGroup", &InspectorRequestBus::Events::RefreshGroup)
->Event("RebuildGroup", &InspectorRequestBus::Events::RebuildGroup)
->Event("RefreshAll", &InspectorRequestBus::Events::RefreshAll)
->Event("RebuildAll", &InspectorRequestBus::Events::RebuildAll)
->Event("ExpandGroup", &InspectorRequestBus::Events::ExpandGroup)
->Event("CollapseGroup", &InspectorRequestBus::Events::CollapseGroup)
->Event("IsGroupExpanded", &InspectorRequestBus::Events::IsGroupExpanded)
->Event("ExpandAll", &InspectorRequestBus::Events::ExpandAll)
->Event("CollapseAll", &InspectorRequestBus::Events::CollapseAll)
;
}
}
InspectorWidget::InspectorWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::InspectorWidget)
{
m_ui->setupUi(this);
SetGroupSettingsPrefix("/O3DE/AtomToolsFramework/InspectorWidget");
}
InspectorWidget::~InspectorWidget()
{
SetSettingsObject(m_collapsedGroupSettingName, m_collapsedGroups);
InspectorRequestBus::Handler::BusDisconnect();
}
void InspectorWidget::AddHeading(QWidget* headingWidget)
@@ -52,6 +94,7 @@ namespace AtomToolsFramework
delete child;
}
m_groups.clear();
InspectorRequestBus::Handler::BusDisconnect();
}
void InspectorWidget::AddGroupsBegin()
@@ -202,20 +245,31 @@ namespace AtomToolsFramework
}
}
void InspectorWidget::SetGroupSettingsPrefix(const AZStd::string& prefix)
{
if (!m_collapsedGroupSettingName.empty())
{
SetSettingsObject(m_collapsedGroupSettingName, m_collapsedGroups);
}
m_collapsedGroupSettingName = prefix + "/CollapsedGroups";
m_collapsedGroups = GetSettingsObject<AZStd::unordered_set<AZ::u32>>(m_collapsedGroupSettingName);
}
bool InspectorWidget::ShouldGroupAutoExpanded(const AZStd::string& groupName) const
{
AZ_UNUSED(groupName);
return true;
auto stateItr = m_collapsedGroups.find(AZ::Crc32(groupName));
return stateItr == m_collapsedGroups.end();
}
void InspectorWidget::OnGroupExpanded(const AZStd::string& groupName)
{
AZ_UNUSED(groupName);
m_collapsedGroups.erase(AZ::Crc32(groupName));
}
void InspectorWidget::OnGroupCollapsed(const AZStd::string& groupName)
{
AZ_UNUSED(groupName);
m_collapsedGroups.insert(AZ::Crc32(groupName));
}
void InspectorWidget::OnHeaderClicked(const AZStd::string& groupName, QMouseEvent* event)
@@ -6,21 +6,18 @@
*
*/
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Reflect/Image/ImageAsset.h>
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/Math/Color.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Math/Vector4.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/UI/PropertyEditor/InstanceDataHierarchy.h>
namespace AtomToolsFramework
{
@@ -223,51 +220,4 @@ namespace AtomToolsFramework
return true;
}
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath)
{
if (referencePath.empty())
{
return {};
}
if (!relativeToExportPath)
{
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
bool sourceInfoFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(),
assetInfo, watchFolder);
if (sourceInfoFound)
{
return assetInfo.m_relativePath;
}
}
AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
exportFolder.RemoveFilename();
return AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix();
}
const AtomToolsFramework::DynamicProperty* FindDynamicPropertyForInstanceDataNode(const AzToolsFramework::InstanceDataNode* pNode)
{
// Traverse up the hierarchy from the input node to search for an instance corresponding to material inspector property
for (const AzToolsFramework::InstanceDataNode* currentNode = pNode; currentNode; currentNode = currentNode->GetParent())
{
const AZ::SerializeContext* context = currentNode->GetSerializeContext();
const AZ::SerializeContext::ClassData* classData = currentNode->GetClassMetadata();
if (context && classData)
{
if (context->CanDowncast(
classData->m_typeId, azrtti_typeid<AtomToolsFramework::DynamicProperty>(), classData->m_azRtti, nullptr))
{
return static_cast<const AtomToolsFramework::DynamicProperty*>(currentNode->FirstInstance());
}
}
}
return nullptr;
}
} // namespace AtomToolsFramework
@@ -8,13 +8,18 @@
#include <Atom/ImageProcessing/ImageObject.h>
#include <Atom/ImageProcessing/ImageProcessingBus.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/IO/ByteContainerStream.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzQtComponents/Components/Widgets/FileDialog.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserBus.h>
#include <AzToolsFramework/AssetBrowser/AssetBrowserEntry.h>
#include <AzToolsFramework/AssetBrowser/AssetSelectionModel.h>
@@ -178,4 +183,74 @@ namespace AtomToolsFramework
return QProcess::startDetached(launchPath.c_str(), arguments, engineRoot.c_str());
}
}
AZStd::string GetExteralReferencePath(
const AZStd::string& exportPath, const AZStd::string& referencePath, const bool relativeToExportPath)
{
if (referencePath.empty())
{
return {};
}
if (!relativeToExportPath)
{
AZStd::string watchFolder;
AZ::Data::AssetInfo assetInfo;
bool sourceInfoFound = false;
AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
sourceInfoFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath, referencePath.c_str(),
assetInfo, watchFolder);
if (sourceInfoFound)
{
return assetInfo.m_relativePath;
}
}
AZ::IO::BasicPath<AZStd::string> exportFolder(exportPath);
exportFolder.RemoveFilename();
return AZ::IO::PathView(referencePath).LexicallyRelative(exportFolder).StringAsPosix();
}
bool SaveSettingsToFile(const AZ::IO::FixedMaxPath& savePath, const AZStd::vector<AZStd::string>& filters)
{
auto registry = AZ::SettingsRegistry::Get();
if (registry == nullptr)
{
AZ_Warning("AtomToolsFramework", false, "Unable to access global settings registry.");
return false;
}
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
dumperSettings.m_prettifyOutput = true;
dumperSettings.m_includeFilter = [filters](AZStd::string_view path)
{
for (const auto& filter : filters)
{
if (filter.starts_with(path.substr(0, filter.size())))
{
return true;
}
}
return false;
};
AZStd::string stringBuffer;
AZ::IO::ByteContainerStream stringStream(&stringBuffer);
if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(*registry, "", stringStream, dumperSettings))
{
AZ_Warning("AtomToolsFramework", false, R"(Unable to save changes to the registry file at "%s"\n)", savePath.c_str());
return false;
}
bool saved = false;
constexpr auto configurationMode =
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY;
if (AZ::IO::SystemFile outputFile; outputFile.Open(savePath.c_str(), configurationMode))
{
saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size();
}
AZ_Warning("AtomToolsFramework", saved, R"(Unable to save registry file to path "%s"\n)", savePath.c_str());
return saved;
}
} // namespace AtomToolsFramework
@@ -8,7 +8,7 @@
#include <AzTest/AzTest.h>
#include <Atom/Utils/TestUtils/AssetSystemStub.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
namespace UnitTest
{
@@ -15,6 +15,7 @@ set(FILES
Include/AtomToolsFramework/Communication/LocalSocket.h
Include/AtomToolsFramework/Debug/TraceRecorder.h
Include/AtomToolsFramework/Document/AtomToolsDocument.h
Include/AtomToolsFramework/Document/AtomToolsDocumentInspector.h
Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h
Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h
Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h
@@ -51,6 +52,7 @@ set(FILES
Source/Communication/LocalSocket.cpp
Source/Debug/TraceRecorder.cpp
Source/Document/AtomToolsDocument.cpp
Source/Document/AtomToolsDocumentInspector.cpp
Source/Document/AtomToolsDocumentApplication.cpp
Source/Document/AtomToolsDocumentMainWindow.cpp
Source/Document/AtomToolsDocumentSystem.cpp
@@ -18,6 +18,7 @@
#include <AtomCore/Instance/Instance.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
@@ -64,7 +64,7 @@ namespace MaterialEditor
m_ui->m_materialTypeComboBox->model()->sort(0, Qt::AscendingOrder);
const AZStd::string defaultMaterialType = AtomToolsFramework::GetSettingOrDefault<AZStd::string>(
const AZStd::string defaultMaterialType = AtomToolsFramework::GetSettingsValue<AZStd::string>(
"/O3DE/Atom/MaterialEditor/CreateMaterialDialog/DefaultMaterialType", "StandardPBR");
const int index = m_ui->m_materialTypeComboBox->findText(defaultMaterialType.c_str());
@@ -9,6 +9,8 @@
#include <Atom/RPI.Edit/Material/MaterialSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicProperty.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/WindowDecorationWrapper.h>
@@ -17,7 +19,6 @@
#include <Window/CreateMaterialDialog/CreateMaterialDialog.h>
#include <Window/MaterialEditorWindow.h>
#include <Window/MaterialEditorWindowSettings.h>
#include <Window/MaterialInspector/MaterialInspector.h>
#include <Window/SettingsDialog/SettingsDialog.h>
#include <Window/ViewportSettingsInspector/ViewportSettingsInspector.h>
@@ -78,7 +79,20 @@ namespace MaterialEditor
QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str()));
});
AddDockWidget("Inspector", new MaterialInspector(m_toolId), Qt::RightDockWidgetArea, Qt::Vertical);
m_materialInspector = new AtomToolsFramework::AtomToolsDocumentInspector(m_toolId, this);
m_materialInspector->SetDocumentSettingsPrefix("/O3DE/Atom/MaterialEditor/MaterialInspector");
m_materialInspector->SetIndicatorFunction(
[](const AzToolsFramework::InstanceDataNode* node)
{
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(node);
if (property && !AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue))
{
return ":/Icons/changed_property.svg";
}
return ":/Icons/blank.png";
});
AddDockWidget("Inspector", m_materialInspector, Qt::RightDockWidgetArea, Qt::Vertical);
AddDockWidget("Viewport Settings", new ViewportSettingsInspector, Qt::LeftDockWidgetArea, Qt::Vertical);
SetDockWidgetVisible("Viewport Settings", false);
@@ -98,6 +112,12 @@ namespace MaterialEditor
OnDocumentOpened(AZ::Uuid::CreateNull());
}
void MaterialEditorWindow::OnDocumentOpened(const AZ::Uuid& documentId)
{
Base::OnDocumentOpened(documentId);
m_materialInspector->SetDocumentId(documentId);
}
void MaterialEditorWindow::ResizeViewportRenderTarget(uint32_t width, uint32_t height)
{
QSize requestedViewportSize = QSize(width, height) / devicePixelRatioF();
@@ -9,6 +9,7 @@
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomToolsFramework/Document/AtomToolsDocumentInspector.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
@@ -35,10 +36,15 @@ namespace MaterialEditor
MaterialEditorWindow(const AZ::Crc32& toolId, QWidget* parent = 0);
protected:
// AtomToolsFramework::AtomToolsMainWindowRequestBus::Handler overrides...
void ResizeViewportRenderTarget(uint32_t width, uint32_t height) override;
void LockViewportRenderTargetSize(uint32_t width, uint32_t height) override;
void UnlockViewportRenderTargetSize() override;
// AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler overrides...
void OnDocumentOpened(const AZ::Uuid& documentId) override;
// AtomToolsFramework::AtomToolsDocumentMainWindow overrides...
bool GetCreateDocumentParams(AZStd::string& openPath, AZStd::string& savePath) override;
bool GetOpenDocumentParams(AZStd::string& openPath) override;
void OpenSettings() override;
@@ -47,6 +53,7 @@ namespace MaterialEditor
void closeEvent(QCloseEvent* closeEvent) override;
AtomToolsFramework::AtomToolsDocumentInspector* m_materialInspector = {};
MaterialViewportWidget* m_materialViewport = {};
MaterialEditorToolBar* m_toolBar = {};
};
@@ -19,7 +19,6 @@ namespace MaterialEditor
serializeContext->Class<MaterialEditorWindowSettings, AZ::UserSettings>()
->Version(1)
->Field("mainWindowState", &MaterialEditorWindowSettings::m_mainWindowState)
->Field("inspectorCollapsedGroups", &MaterialEditorWindowSettings::m_inspectorCollapsedGroups)
;
if (auto editContext = serializeContext->GetEditContext())
@@ -27,6 +27,5 @@ namespace MaterialEditor
static void Reflect(AZ::ReflectContext* context);
AZStd::vector<char> m_mainWindowState;
AZStd::unordered_set<AZ::u32> m_inspectorCollapsedGroups;
};
} // namespace MaterialEditor
@@ -1,159 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <Window/MaterialInspector/MaterialInspector.h>
namespace MaterialEditor
{
MaterialInspector::MaterialInspector(const AZ::Crc32& toolId, QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
, m_toolId(toolId)
{
m_windowSettings = AZ::UserSettings::CreateFind<MaterialEditorWindowSettings>(
AZ::Crc32("MaterialEditorWindowSettings"), AZ::UserSettings::CT_GLOBAL);
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
}
MaterialInspector::~MaterialInspector()
{
AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void MaterialInspector::Reset()
{
m_documentPath.clear();
m_documentId = AZ::Uuid::CreateNull();
m_activeProperty = {};
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
bool MaterialInspector::ShouldGroupAutoExpanded(const AZStd::string& groupName) const
{
auto stateItr = m_windowSettings->m_inspectorCollapsedGroups.find(GetGroupSaveStateKey(groupName));
return stateItr == m_windowSettings->m_inspectorCollapsedGroups.end();
}
void MaterialInspector::OnGroupExpanded(const AZStd::string& groupName)
{
m_windowSettings->m_inspectorCollapsedGroups.erase(GetGroupSaveStateKey(groupName));
}
void MaterialInspector::OnGroupCollapsed(const AZStd::string& groupName)
{
m_windowSettings->m_inspectorCollapsedGroups.insert(GetGroupSaveStateKey(groupName));
}
void MaterialInspector::OnDocumentOpened(const AZ::Uuid& documentId)
{
AddGroupsBegin();
m_documentId = documentId;
bool isOpen = false;
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(
isOpen, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::IsOpen);
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(
m_documentPath, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
if (!m_documentId.IsNull() && isOpen)
{
// This will automatically expose all document contents to an inspector with a collapsible group per object. In the case of the
// material editor, this will be one inspector group per property group.
AZStd::vector<AtomToolsFramework::DocumentObjectInfo> objects;
AtomToolsFramework::AtomToolsDocumentRequestBus::EventResult(
objects, m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::GetObjectInfo);
for (auto& objectInfo : objects)
{
// Passing in same main and comparison instance to enable custom value comparison for highlighting modified properties
auto propertyGroupWidget = new AtomToolsFramework::InspectorPropertyGroupWidget(
objectInfo.m_objectPtr, objectInfo.m_objectPtr, objectInfo.m_objectType, this, this,
GetGroupSaveStateKey(objectInfo.m_name), {},
[this](const auto node) { return GetInstanceNodePropertyIndicator(node); }, 0);
AddGroup(objectInfo.m_name, objectInfo.m_displayName, objectInfo.m_description, propertyGroupWidget);
SetGroupVisible(objectInfo.m_name, objectInfo.m_visible);
}
AtomToolsFramework::InspectorRequestBus::Handler::BusConnect(m_documentId);
}
AddGroupsEnd();
}
AZ::Crc32 MaterialInspector::GetGroupSaveStateKey(const AZStd::string& groupName) const
{
return AZ::Crc32(AZStd::string::format("MaterialInspector::PropertyGroup::%s::%s", m_documentPath.c_str(), groupName.c_str()));
}
bool MaterialInspector::IsInstanceNodePropertyModifed(const AzToolsFramework::InstanceDataNode* node) const
{
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(node);
return property && !AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue);
}
const char* MaterialInspector::GetInstanceNodePropertyIndicator(const AzToolsFramework::InstanceDataNode* node) const
{
if (IsInstanceNodePropertyModifed(node))
{
return ":/Icons/changed_property.svg";
}
return ":/Icons/blank.png";
}
void MaterialInspector::OnDocumentObjectInfoChanged(
[[maybe_unused]] const AZ::Uuid& documentId, const AtomToolsFramework::DocumentObjectInfo& objectInfo, bool rebuilt)
{
SetGroupVisible(objectInfo.m_name, objectInfo.m_visible);
if (rebuilt)
{
RebuildGroup(objectInfo.m_name);
}
else
{
RefreshGroup(objectInfo.m_name);
}
}
void MaterialInspector::BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
// This function is called before every single property change whether it's a button click or dragging a slider. We only want to
// begin tracking undo state for the first change in the sequence, when the user begins to drag the slider.
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
if (!m_activeProperty && property)
{
m_activeProperty = property;
AtomToolsFramework::AtomToolsDocumentRequestBus::Event(
m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::BeginEdit);
}
}
void MaterialInspector::SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode)
{
// If tracking has started and editing has completed then we can stop tracking undue state for this sequence of changes.
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
if (m_activeProperty && m_activeProperty == property)
{
AtomToolsFramework::AtomToolsDocumentRequestBus::Event(
m_documentId, &AtomToolsFramework::AtomToolsDocumentRequestBus::Events::EndEdit);
m_activeProperty = {};
}
}
} // namespace MaterialEditor
#include <Window/MaterialInspector/moc_MaterialInspector.cpp>
@@ -1,71 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#if !defined(Q_MOC_RUN)
#include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
#include <AtomToolsFramework/DynamicProperty/DynamicPropertyGroup.h>
#include <AtomToolsFramework/Inspector/InspectorWidget.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#include <Window/MaterialEditorWindowSettings.h>
#endif
namespace MaterialEditor
{
//! Provides controls for viewing and editing document settings.
class MaterialInspector
: public AtomToolsFramework::InspectorWidget
, public AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler
, public AzToolsFramework::IPropertyEditorNotify
{
Q_OBJECT
public:
AZ_CLASS_ALLOCATOR(MaterialInspector, AZ::SystemAllocator, 0);
MaterialInspector(const AZ::Crc32& toolId, QWidget* parent = nullptr);
~MaterialInspector() override;
// AtomToolsFramework::InspectorRequestBus::Handler overrides...
void Reset() override;
protected:
bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const override;
void OnGroupExpanded(const AZStd::string& groupName) override;
void OnGroupCollapsed(const AZStd::string& groupName) override;
private:
AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupName) const;
bool IsInstanceNodePropertyModifed(const AzToolsFramework::InstanceDataNode* node) const;
const char* GetInstanceNodePropertyIndicator(const AzToolsFramework::InstanceDataNode* node) const;
// AtomToolsDocumentNotificationBus::Handler implementation
void OnDocumentOpened(const AZ::Uuid& documentId) override;
void OnDocumentObjectInfoChanged(
const AZ::Uuid& documentId, const AtomToolsFramework::DocumentObjectInfo& objectInfo, bool rebuilt) override;
// AzToolsFramework::IPropertyEditorNotify overrides...
void BeforePropertyModified(AzToolsFramework::InstanceDataNode* pNode) override;
void AfterPropertyModified([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingActive([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode) override {}
void SetPropertyEditingComplete(AzToolsFramework::InstanceDataNode* pNode) override;
void SealUndoStack() override {}
void RequestPropertyContextMenu([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, const QPoint&) override {}
void PropertySelectionChanged([[maybe_unused]] AzToolsFramework::InstanceDataNode* pNode, bool) override {}
const AZ::Crc32 m_toolId = {};
// Tracking the property that is activiley being edited in the inspector
const AtomToolsFramework::DynamicProperty* m_activeProperty = {};
AZ::Uuid m_documentId = AZ::Uuid::CreateNull();
AZStd::string m_documentPath;
AZStd::intrusive_ptr<MaterialEditorWindowSettings> m_windowSettings;
};
} // namespace MaterialEditor
@@ -6,19 +6,19 @@
*
*/
#include <Window/SettingsDialog/SettingsWidget.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
#include <Window/SettingsDialog/SettingsWidget.h>
namespace MaterialEditor
{
SettingsWidget::SettingsWidget(QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
{
SetGroupSettingsPrefix("/O3DE/Atom/MaterialEditor/SettingsWidget");
}
SettingsWidget::~SettingsWidget()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void SettingsWidget::Populate()
@@ -29,7 +29,6 @@ namespace MaterialEditor
void SettingsWidget::Reset()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
} // namespace MaterialEditor
@@ -32,9 +32,7 @@ namespace MaterialEditor
m_viewportSettings =
AZ::UserSettings::CreateFind<MaterialViewportSettings>(AZ::Crc32("MaterialViewportSettings"), AZ::UserSettings::CT_GLOBAL);
m_windowSettings = AZ::UserSettings::CreateFind<MaterialEditorWindowSettings>(
AZ::Crc32("MaterialEditorWindowSettings"), AZ::UserSettings::CT_GLOBAL);
SetGroupSettingsPrefix("/O3DE/Atom/MaterialEditor/ViewportSettingsInspector");
MaterialViewportNotificationBus::Handler::BusConnect();
}
@@ -43,7 +41,6 @@ namespace MaterialEditor
m_lightingPreset.reset();
m_modelPreset.reset();
MaterialViewportNotificationBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void ViewportSettingsInspector::Populate()
@@ -144,7 +141,7 @@ namespace MaterialEditor
});
const int itemSize = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/MaterialEditor/AssetGridDialog/ModelItemSize", 180));
AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/Atom/MaterialEditor/AssetGridDialog/ModelItemSize", 180));
AtomToolsFramework::AssetGridDialog dialog(
"Model Preset Browser", selectableAssets, selectedAsset, QSize(itemSize, itemSize), QApplication::activeWindow());
@@ -270,7 +267,7 @@ namespace MaterialEditor
});
const int itemSize = aznumeric_cast<int>(
AtomToolsFramework::GetSettingOrDefault<AZ::u64>("/O3DE/Atom/MaterialEditor/AssetGridDialog/LightingItemSize", 180));
AtomToolsFramework::GetSettingsValue<AZ::u64>("/O3DE/Atom/MaterialEditor/AssetGridDialog/LightingItemSize", 180));
AtomToolsFramework::AssetGridDialog dialog(
"Lighting Preset Browser", selectableAssets, selectedAsset, QSize(itemSize, itemSize), QApplication::activeWindow());
@@ -338,7 +335,6 @@ namespace MaterialEditor
m_viewportSettings->m_displayMapperOperationType = viewportRequests->GetDisplayMapperOperationType();
});
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
@@ -433,23 +429,6 @@ namespace MaterialEditor
{
return AZ::Crc32(AZStd::string::format("ViewportSettingsInspector::PropertyGroup::%s", groupName.c_str()));
}
bool ViewportSettingsInspector::ShouldGroupAutoExpanded(const AZStd::string& groupName) const
{
auto stateItr = m_windowSettings->m_inspectorCollapsedGroups.find(GetGroupSaveStateKey(groupName));
return stateItr == m_windowSettings->m_inspectorCollapsedGroups.end();
}
void ViewportSettingsInspector::OnGroupExpanded(const AZStd::string& groupName)
{
m_windowSettings->m_inspectorCollapsedGroups.erase(GetGroupSaveStateKey(groupName));
}
void ViewportSettingsInspector::OnGroupCollapsed(const AZStd::string& groupName)
{
m_windowSettings->m_inspectorCollapsedGroups.insert(GetGroupSaveStateKey(groupName));
}
} // namespace MaterialEditor
#include <Window/ViewportSettingsInspector/moc_ViewportSettingsInspector.cpp>
@@ -16,7 +16,6 @@
#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI_Internals.h>
#include <Viewport/MaterialViewportNotificationBus.h>
#include <Viewport/MaterialViewportSettings.h>
#include <Window/MaterialEditorWindowSettings.h>
#endif
namespace MaterialEditor
@@ -75,13 +74,9 @@ namespace MaterialEditor
AZStd::string GetDefaultUniqueSaveFilePath(const AZStd::string& baseName) const;
AZ::Crc32 GetGroupSaveStateKey(const AZStd::string& groupName) const;
bool ShouldGroupAutoExpanded(const AZStd::string& groupName) const override;
void OnGroupExpanded(const AZStd::string& groupName) override;
void OnGroupCollapsed(const AZStd::string& groupName) override;
AZ::Render::ModelPresetPtr m_modelPreset;
AZ::Render::LightingPresetPtr m_lightingPreset;
AZStd::intrusive_ptr<MaterialViewportSettings> m_viewportSettings;
AZStd::intrusive_ptr<MaterialEditorWindowSettings> m_windowSettings;
};
} // namespace MaterialEditor
@@ -46,8 +46,6 @@ set(FILES
Source/Window/ToolBar/ModelPresetComboBox.cpp
Source/Window/ToolBar/LightingPresetComboBox.h
Source/Window/ToolBar/LightingPresetComboBox.cpp
Source/Window/MaterialInspector/MaterialInspector.h
Source/Window/MaterialInspector/MaterialInspector.cpp
Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.h
Source/Window/ViewportSettingsInspector/ViewportSettingsInspector.cpp
)
@@ -14,8 +14,11 @@
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
#include <Atom/RPI.Edit/Material/MaterialUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialFunctor.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
#include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConfig.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
@@ -24,9 +27,6 @@
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConfig.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QApplication>
@@ -54,7 +54,6 @@ namespace AZ
MaterialPropertyInspector::~MaterialPropertyInspector()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
AZ::EntitySystemBus::Handler::BusDisconnect();
EditorMaterialSystemComponentNotificationBus::Handler::BusDisconnect();
@@ -137,7 +136,6 @@ namespace AZ
m_dirtyPropertyFlags.set();
m_internalEditNotification = {};
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
AtomToolsFramework::InspectorWidget::Reset();
}
@@ -590,7 +588,7 @@ namespace AZ
bool MaterialPropertyInspector::IsInstanceNodePropertyModifed(const AzToolsFramework::InstanceDataNode* node) const
{
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(node);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(node);
return property && !AtomToolsFramework::ArePropertyValuesEqual(property->GetValue(), property->GetConfig().m_parentValue);
}
@@ -719,7 +717,7 @@ namespace AZ
// This function is called continuously anytime a property changes until the edit has completed
// Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has
// started and ended
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property)
{
if (m_activeProperty != property)
@@ -731,7 +729,7 @@ namespace AZ
void MaterialPropertyInspector::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property)
{
if (m_activeProperty == property)
@@ -748,7 +746,7 @@ namespace AZ
// As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been
// completed but they are not being called following that pattern. when this function executes the changes to the property
// are ready to be committed or reverted
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property)
{
if (m_activeProperty == property)
@@ -15,10 +15,11 @@
#include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
#include <Atom/RPI.Edit/Material/MaterialUtils.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialNameContext.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
@@ -6,10 +6,6 @@
*
*/
#include <Material/EditorMaterialModelUvNameMapInspector.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <Atom/RPI.Edit/Common/JsonUtils.h>
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
@@ -19,11 +15,13 @@
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <Atom/RPI.Reflect/Material/MaterialPropertiesLayout.h>
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
#include <AtomToolsFramework/Inspector/InspectorPropertyGroupWidget.h>
#include <AtomToolsFramework/Util/MaterialPropertyUtil.h>
#include <AtomToolsFramework/Util/Util.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
#include <AzToolsFramework/API/EditorWindowRequestBus.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <Material/EditorMaterialModelUvNameMapInspector.h>
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
#include <QApplication>
@@ -47,15 +45,16 @@ namespace AZ
const RPI::MaterialModelUvOverrideMap& matModUvOverrides,
const AZStd::unordered_set<AZ::Name>& modelUvNames,
MaterialModelUvOverrideMapChangedCallBack matModUvOverrideMapChangedCallBack,
QWidget* parent
)
QWidget* parent)
: AtomToolsFramework::InspectorWidget(parent)
, m_matModUvOverrideMapChangedCallBack(matModUvOverrideMapChangedCallBack)
, m_matModUvOverrides(matModUvOverrides)
{
// Load the originating product asset from which the new source has set will be generated
auto materialAssetOutcome = AZ::RPI::AssetUtils::LoadAsset<AZ::RPI::MaterialAsset>(assetId);
AZ_Error("AZ::Render::EditorMaterialComponentInspector", materialAssetOutcome, "Failed to load material asset: %s", assetId.ToString<AZStd::string>().c_str());
AZ_Error(
"AZ::Render::EditorMaterialComponentInspector", materialAssetOutcome, "Failed to load material asset: %s",
assetId.ToString<AZStd::string>().c_str());
auto materialAsset = materialAssetOutcome.GetValue();
@@ -68,12 +67,13 @@ namespace AZ
MaterialModelUvNameMapInspector::~MaterialModelUvNameMapInspector()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
}
void MaterialModelUvNameMapInspector::Reset()
{
AtomToolsFramework::InspectorRequestBus::Handler::BusDisconnect();
m_activeProperty = {};
m_group = {};
AtomToolsFramework::InspectorWidget::Reset();
}
@@ -119,7 +119,7 @@ namespace AZ
// For some reason the reflected property editor notifications are not symmetrical
// This function is called continuously anytime a property changes until the edit has completed
// Because of that, we have to track whether or not we are continuing to edit the same property to know when editing has started and ended
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property && m_activeProperty != property)
{
m_activeProperty = property;
@@ -128,7 +128,7 @@ namespace AZ
void MaterialModelUvNameMapInspector::AfterPropertyModified(AzToolsFramework::InstanceDataNode* pNode)
{
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property && m_activeProperty == property)
{
uint32_t index = 0;
@@ -159,7 +159,7 @@ namespace AZ
{
// As above, there are symmetrical functions on the notification interface for when editing begins and ends and has been completed but they are not being called following that pattern.
// when this function executes the changes to the property are ready to be committed or reverted
const AtomToolsFramework::DynamicProperty* property = AtomToolsFramework::FindDynamicPropertyForInstanceDataNode(pNode);
const auto property = AtomToolsFramework::FindAncestorInstanceDataNodeByType<AtomToolsFramework::DynamicProperty>(pNode);
if (property && m_activeProperty == property)
{
uint32_t index = 0;
@@ -53,9 +53,9 @@ namespace AZ
thumbnailConfig.m_modelId = assetInfo.m_assetId;
thumbnailConfig.m_materialId = SharedPreviewUtils::GetAssetIdForProductPath(
AtomToolsFramework::GetSettingOrDefault<AZStd::string>(MaterialAssetPathSetting, DefaultMaterialPath));
AtomToolsFramework::GetSettingsValue<AZStd::string>(MaterialAssetPathSetting, DefaultMaterialPath));
thumbnailConfig.m_lightingId = SharedPreviewUtils::GetAssetIdForProductPath(
AtomToolsFramework::GetSettingOrDefault<AZStd::string>(LightingAssetPathSetting, DefaultLightingPresetPath));
AtomToolsFramework::GetSettingsValue<AZStd::string>(LightingAssetPathSetting, DefaultLightingPresetPath));
}
else if (assetInfo.m_assetType == RPI::MaterialAsset::RTTI_Type())
{
@@ -65,10 +65,10 @@ namespace AZ
"/O3DE/Atom/CommonFeature/SharedPreview/MaterialAssetType/LightingAssetPath";
thumbnailConfig.m_modelId = SharedPreviewUtils::GetAssetIdForProductPath(
AtomToolsFramework::GetSettingOrDefault<AZStd::string>(ModelAssetPathSetting, DefaultModelPath));
AtomToolsFramework::GetSettingsValue<AZStd::string>(ModelAssetPathSetting, DefaultModelPath));
thumbnailConfig.m_materialId = assetInfo.m_assetId;
thumbnailConfig.m_lightingId = SharedPreviewUtils::GetAssetIdForProductPath(
AtomToolsFramework::GetSettingOrDefault<AZStd::string>(LightingAssetPathSetting, DefaultLightingPresetPath));
AtomToolsFramework::GetSettingsValue<AZStd::string>(LightingAssetPathSetting, DefaultLightingPresetPath));
}
else if (assetInfo.m_assetType == RPI::AnyAsset::RTTI_Type())
{
@@ -78,9 +78,9 @@ namespace AZ
"/O3DE/Atom/CommonFeature/SharedPreview/LightingAssetType/MaterialAssetPath";
thumbnailConfig.m_modelId = SharedPreviewUtils::GetAssetIdForProductPath(
AtomToolsFramework::GetSettingOrDefault<AZStd::string>(ModelAssetPathSetting, DefaultModelPath));
AtomToolsFramework::GetSettingsValue<AZStd::string>(ModelAssetPathSetting, DefaultModelPath));
thumbnailConfig.m_materialId = SharedPreviewUtils::GetAssetIdForProductPath(
AtomToolsFramework::GetSettingOrDefault<AZStd::string>(MaterialAssetPathSetting, "materials/reflectionprobe/reflectionprobevisualization.azmaterial"));
AtomToolsFramework::GetSettingsValue<AZStd::string>(MaterialAssetPathSetting, "materials/reflectionprobe/reflectionprobevisualization.azmaterial"));
thumbnailConfig.m_lightingId = assetInfo.m_assetId;
}