Pārlūkot izejas kodu

Atom Tools: updated document and windows systems and buses to support multiple instances
• This change is partially to unblock physics tool prototyping. It introduces a tool ID that is passed down into systems and acts as a context for document, window, and other systems and buses.
• The document system component is no longer a component. It is just a system class that can be constructed with a tool ID. Internally, it will connect to its buses and be addressable by tool ID. More than one can be instantiated, each with a unique tool ID.
• These changes are still backward compatible because most of the buses were using broadcast for standalone applications. All of those calls have been updated but not all of the scripts, which should still work as is.
• Got rid of the window factory request bus in favor of just instantiating the main window or any other UI in the application layer.
• Fixed a couple of bugs that were discovered while making these changes.

Signed-off-by: Guthrie Adams <[email protected]>

Guthrie Adams 3 gadi atpakaļ
vecāks
revīzija
c6ba1ef064
39 mainītis faili ar 554 papildinājumiem un 602 dzēšanām
  1. 3 3
      AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py
  2. 1 1
      AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py
  3. 7 22
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h
  4. 5 3
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h
  5. 10 2
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h
  6. 1 1
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h
  7. 2 1
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h
  8. 25 43
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h
  9. 7 4
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h
  10. 4 1
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindow.h
  11. 0 30
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h
  12. 2 1
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h
  13. 6 3
      Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h
  14. 63 72
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp
  15. 23 18
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetBrowser/AtomToolsAssetBrowser.cpp
  16. 0 3
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp
  17. 25 24
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp
  18. 22 4
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentApplication.cpp
  19. 31 23
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentMainWindow.cpp
  20. 175 173
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystem.cpp
  21. 33 14
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindow.cpp
  22. 0 9
      Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindowSystemComponent.cpp
  23. 2 3
      Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake
  24. 8 8
      Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp
  25. 2 2
      Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h
  26. 32 39
      Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp
  27. 1 7
      Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h
  28. 3 2
      Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp
  29. 3 1
      Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h
  30. 7 22
      Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.cpp
  31. 1 1
      Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.h
  32. 3 2
      Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp
  33. 3 1
      Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h
  34. 4 4
      Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.cpp
  35. 3 3
      Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h
  36. 30 37
      Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp
  37. 1 7
      Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h
  38. 5 7
      Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp
  39. 1 1
      Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h

+ 3 - 3
AutomatedTesting/Gem/PythonTests/Atom/atom_utils/material_editor_utils.py

@@ -162,11 +162,11 @@ def select_model_config(configname):
     azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectModelPresetByName", configname)
 
 
-def destroy_main_window():
+def exit():
     """
-    Closes the Material Editor window
+    Closes the Material Editor
     """
-    azlmbr.atomtools.AtomToolsMainWindowFactoryRequestBus(azlmbr.bus.Broadcast, "DestroyMainWindow")
+    azlmbr.atomtools.general.exit()
 
 
 def wait_for_condition(function, timeout_in_seconds=1.0):

+ 1 - 1
AutomatedTesting/Gem/PythonTests/Atom/tests/hydra_AtomMaterialEditor_BasicTests.py

@@ -214,7 +214,7 @@ def run():
                                        (not material_editor.is_open(document1_id)) and
                                        (not material_editor.is_open(document2_id)) and
                                        (not material_editor.is_open(document3_id)), 2.0)
-    material_editor.destroy_main_window()
+    material_editor.exit()
 
 
 if __name__ == "__main__":

+ 7 - 22
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Application/AtomToolsApplication.h

@@ -39,16 +39,16 @@ namespace AtomToolsFramework
     {
     public:
         AZ_TYPE_INFO(AtomTools::AtomToolsApplication, "{A0DF25BA-6F74-4F11-9F85-0F99278D5986}");
+        AZ_DISABLE_COPY_MOVE(AtomToolsApplication);
 
         using Base = AzFramework::Application;
 
-        AtomToolsApplication(int* argc, char*** argv);
+        AtomToolsApplication(const AZStd::string& targetName, int* argc, char*** argv);
         ~AtomToolsApplication();
 
         virtual bool LaunchLocalServer();
 
-        //////////////////////////////////////////////////////////////////////////
-        // AzFramework::Application
+        // AzFramework::Application overrides...
         void CreateReflectionManager() override;
         void Reflect(AZ::ReflectContext* context) override;
         void RegisterCoreComponents() override;
@@ -57,43 +57,25 @@ namespace AtomToolsFramework
         const char* GetCurrentConfigurationName() const override;
         void StartCommon(AZ::Entity* systemEntity) override;
         void Tick() override;
-        void Stop() override;
+        void Destroy() override;
 
     protected:
-        //////////////////////////////////////////////////////////////////////////
         // AtomsToolMainWindowNotificationBus::Handler overrides...
         void OnMainWindowClosing() override;
-        //////////////////////////////////////////////////////////////////////////
 
-        //////////////////////////////////////////////////////////////////////////
         // AssetDatabaseRequestsBus::Handler overrides...
         bool GetAssetDatabaseLocation(AZStd::string& result) override;
-        //////////////////////////////////////////////////////////////////////////
 
-        //////////////////////////////////////////////////////////////////////////
-        // AzFramework::Application overrides...
-        void Destroy() override;
-        //////////////////////////////////////////////////////////////////////////
-
-        //////////////////////////////////////////////////////////////////////////
         // AZ::ComponentApplication overrides...
         void QueryApplicationType(AZ::ApplicationTypeQuery& appType) const override;
-        //////////////////////////////////////////////////////////////////////////
 
-        //////////////////////////////////////////////////////////////////////////
         // AZ::UserSettingsOwnerRequestBus::Handler overrides...
         void SaveSettings() override;
-        //////////////////////////////////////////////////////////////////////////
 
-        ////////////////////////////////////////////////////////////////////////
         // EditorPythonConsoleNotificationBus::Handler overrides...
         void OnTraceMessage(AZStd::string_view message) override;
         void OnErrorMessage(AZStd::string_view message) override;
         void OnExceptionMessage(AZStd::string_view message) override;
-        ////////////////////////////////////////////////////////////////////////
-
-        //! Executable target name generally used as a prefix for logging and other saved files
-        virtual AZStd::string GetBuildTargetName() const;
 
         //! List of filters for assets that need to be pre-built to run the application
         virtual AZStd::vector<AZStd::string> GetCriticalAssetFilters() const;
@@ -121,5 +103,8 @@ namespace AtomToolsFramework
 
         AtomToolsFramework::LocalSocket m_socket;
         AtomToolsFramework::LocalServer m_server;
+
+        const AZStd::string m_targetName;
+        const AZ::Crc32 m_toolId = {};
     };
 } // namespace AtomToolsFramework

+ 5 - 3
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocument.h

@@ -23,9 +23,9 @@ namespace AtomToolsFramework
     public:
         AZ_RTTI(AtomToolsDocument, "{8992DF74-88EC-438C-B280-6E71D4C0880B}");
         AZ_CLASS_ALLOCATOR(AtomToolsDocument, AZ::SystemAllocator, 0);
-        AZ_DISABLE_COPY(AtomToolsDocument);
+        AZ_DISABLE_COPY_MOVE(AtomToolsDocument);
 
-        AtomToolsDocument();
+        AtomToolsDocument(const AZ::Crc32& toolId);
         virtual ~AtomToolsDocument();
 
         const AZ::Uuid& GetId() const;
@@ -66,8 +66,10 @@ namespace AtomToolsFramework
         //! This can be overridden to restore additional data.
         virtual bool ReopenRestoreState();
 
+        const AZ::Crc32 m_toolId = {};
+
         //! The unique id of this document, used for all bus notifications and requests.
-        AZ::Uuid m_id = AZ::Uuid::CreateRandom();
+        const AZ::Uuid m_id = AZ::Uuid::CreateRandom();
 
         //! The absolute path to the document source file.
         AZStd::string m_absolutePath;

+ 10 - 2
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h

@@ -9,6 +9,7 @@
 #pragma once
 
 #include <AtomToolsFramework/Application/AtomToolsApplication.h>
+#include <AtomToolsFramework/Document/AtomToolsDocumentSystem.h>
 
 namespace AtomToolsFramework
 {
@@ -16,13 +17,20 @@ namespace AtomToolsFramework
         : public AtomToolsApplication
     {
     public:
-        AZ_TYPE_INFO(AtomToolsDocumentApplication, "{F4B43677-EB95-4CBB-8B8E-9EF4247E6F0D}");
+        AZ_TYPE_INFO(AtomToolsDocumentApplication, "{AC892170-D353-404A-A3D8-BB039C717295}");
+        AZ_DISABLE_COPY_MOVE(AtomToolsDocumentApplication);
 
         using Base = AtomToolsApplication;
 
-        AtomToolsDocumentApplication(int* argc, char*** argv);
+        AtomToolsDocumentApplication(const AZStd::string& targetName, int* argc, char*** argv);
 
+    protected:
         // AtomToolsApplication overrides...
+        void Reflect(AZ::ReflectContext* context) override;
+        void StartCommon(AZ::Entity* systemEntity) override;
+        void Destroy() override;
         void ProcessCommandLine(const AZ::CommandLine& commandLine) override;
+
+        AZStd::unique_ptr<AtomToolsDocumentSystem> m_documentSystem; 
     };
 } // namespace AtomToolsFramework

+ 1 - 1
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h

@@ -28,7 +28,7 @@ namespace AtomToolsFramework
 
         using Base = AtomToolsMainWindow;
 
-        AtomToolsDocumentMainWindow(QWidget* parent = 0);
+        AtomToolsDocumentMainWindow(const AZ::Crc32& toolId, QWidget* parent = 0);
         ~AtomToolsDocumentMainWindow();
 
     protected:

+ 2 - 1
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h

@@ -18,8 +18,9 @@ namespace AtomToolsFramework
         : public AZ::EBusTraits
     {
     public:
-        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
         static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+        typedef AZ::Crc32 BusIdType;
 
         //! Signal that a document was created
         //! @param documentId unique id of document for which the notification is sent

+ 25 - 43
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.h → Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h

@@ -8,60 +8,34 @@
 
 #pragma once
 
-#include <AzCore/Component/Component.h>
-#include <AzCore/std/smart_ptr/shared_ptr.h>
-#include <AzCore/Asset/AssetCommon.h>
-
 #include <AtomToolsFramework/Document/AtomToolsDocument.h>
 #include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
 #include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
-
-AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
-#include <QFileInfo>
-#include <QString>
-AZ_POP_DISABLE_WARNING
+#include <AzCore/Asset/AssetCommon.h>
+#include <AzCore/Memory/Memory.h>
+#include <AzCore/RTTI/RTTI.h>
+#include <AzCore/std/smart_ptr/shared_ptr.h>
 
 namespace AtomToolsFramework
 {
-    //! AtomToolsDocumentSystemComponent is the central component for managing documents
-    class AtomToolsDocumentSystemComponent
-        : public AZ::Component
-        , private AtomToolsDocumentNotificationBus::Handler
-        , private AtomToolsDocumentSystemRequestBus::Handler
+    //! AtomToolsDocumentSystem Is responsible for creation, management, and requests related to documents
+    class AtomToolsDocumentSystem
+        : public AtomToolsDocumentNotificationBus::Handler
+        , public AtomToolsDocumentSystemRequestBus::Handler
     {
     public:
-        AZ_COMPONENT(AtomToolsDocumentSystemComponent, "{343A3383-6A59-4343-851B-BF84FC6CB18E}");
-
-        AtomToolsDocumentSystemComponent();
-        ~AtomToolsDocumentSystemComponent() = default;
-        AtomToolsDocumentSystemComponent(const AtomToolsDocumentSystemComponent&) = delete;
-        AtomToolsDocumentSystemComponent& operator=(const AtomToolsDocumentSystemComponent&) = delete;
+        AZ_CLASS_ALLOCATOR(AtomToolsFramework::AtomToolsDocumentSystem, AZ::SystemAllocator, 0);
+        AZ_RTTI(AtomToolsFramework::AtomToolsDocumentSystem, "{9D31F309-6B20-40C5-813C-F1226180E1F8}");
+        AZ_DISABLE_COPY_MOVE(AtomToolsDocumentSystem);
 
         static void Reflect(AZ::ReflectContext* context);
 
-        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
-        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
-
-    private:
-        ////////////////////////////////////////////////////////////////////////
-        // AZ::Component interface implementation
-        void Init() override;
-        void Activate() override;
-        void Deactivate() override;
-        ////////////////////////////////////////////////////////////////////////
-
-        //////////////////////////////////////////////////////////////////////////
-        // AtomToolsDocumentNotificationBus::Handler overrides...
-        void OnDocumentDependencyModified(const AZ::Uuid& documentId) override;
-        void OnDocumentExternallyModified(const AZ::Uuid& documentId) override;
-        //////////////////////////////////////////////////////////////////////////
-
-        void QueueReopenDocuments();
-        void ReopenDocuments();
+        AtomToolsDocumentSystem() = default;
+        AtomToolsDocumentSystem(const AZ::Crc32& toolId);
+        ~AtomToolsDocumentSystem();
 
-        ////////////////////////////////////////////////////////////////////////
         // AtomToolsDocumentSystemRequestBus::Handler overrides...
-        void RegisterDocumentType(AZStd::function<AtomToolsDocument*()> documentCreator) override;
+        void RegisterDocumentType(const AtomToolsDocumentFactoryCallback& documentCreator) override;
         AZ::Uuid CreateDocument() override;
         bool DestroyDocument(const AZ::Uuid& documentId) override;
         AZ::Uuid OpenDocument(AZStd::string_view sourcePath) override;
@@ -74,11 +48,19 @@ namespace AtomToolsFramework
         bool SaveDocumentAsChild(const AZ::Uuid& documentId, AZStd::string_view targetPath) override;
         bool SaveAllDocuments() override;
         AZ::u32 GetDocumentCount() const override;
-        ////////////////////////////////////////////////////////////////////////
+
+    private:
+        // AtomToolsDocumentNotificationBus::Handler overrides...
+        void OnDocumentDependencyModified(const AZ::Uuid& documentId) override;
+        void OnDocumentExternallyModified(const AZ::Uuid& documentId) override;
+
+        void QueueReopenDocuments();
+        void ReopenDocuments();
 
         AZ::Uuid OpenDocumentImpl(AZStd::string_view sourcePath, bool checkIfAlreadyOpen);
 
-        AZStd::function<AtomToolsDocument*()> m_documentCreator;
+        const AZ::Crc32 m_toolId = {};
+        AtomToolsDocumentFactoryCallback m_documentCreator;
         AZStd::unordered_map<AZ::Uuid, AZStd::shared_ptr<AtomToolsDocument>> m_documentMap;
         AZStd::unordered_set<AZ::Uuid> m_documentIdsWithExternalChanges;
         AZStd::unordered_set<AZ::Uuid> m_documentIdsWithDependencyChanges;

+ 7 - 4
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h

@@ -14,16 +14,19 @@ namespace AtomToolsFramework
 {
     class AtomToolsDocument;
 
-    //! AtomToolsDocumentSystemRequestBus provides high level requests for menus, scripts, etc.
+    using AtomToolsDocumentFactoryCallback = AZStd::function<AtomToolsDocument*(const AZ::Crc32&)>;
+
+    //! AtomToolsDocumentSystemRequestBus is an interface that provides requests for high level user interactions with a system of documents
     class AtomToolsDocumentSystemRequests
         : public AZ::EBusTraits
     {
     public:
-        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
-        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+        typedef AZ::Crc32 BusIdType;
 
         //! Register a document factory function used to create specific document types
-        virtual void RegisterDocumentType(AZStd::function<AtomToolsDocument*()> documentCreator) = 0;
+        virtual void RegisterDocumentType(const AtomToolsDocumentFactoryCallback& documentCreator) = 0;
 
         //! Create a document
         //! @return Uuid of new document, or null Uuid if failed

+ 4 - 1
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindow.h

@@ -25,7 +25,7 @@ namespace AtomToolsFramework
         , protected AtomToolsMainWindowRequestBus::Handler
     {
     public:
-        AtomToolsMainWindow(QWidget* parent = 0);
+        AtomToolsMainWindow(const AZ::Crc32& toolId, QWidget* parent = 0);
         ~AtomToolsMainWindow();
 
     protected:
@@ -48,6 +48,9 @@ namespace AtomToolsFramework
 
         virtual void SetupMetrics();
         virtual void UpdateMetrics();
+        virtual void UpdateWindowTitle();
+
+        const AZ::Crc32 m_toolId = {};
 
         AzQtComponents::FancyDocking* m_advancedDockManager = {};
 

+ 0 - 30
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h

@@ -1,30 +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
-
-#include <AzCore/EBus/EBus.h>
-
-namespace AtomToolsFramework
-{
-    //! AtomToolsMainWindowFactoryRequestBus provides
-    class AtomToolsMainWindowFactoryRequests : public AZ::EBusTraits
-    {
-    public:
-        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
-        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
-
-        /// Creates and shows main window
-        virtual void CreateMainWindow() = 0;
-
-        //! Destroys main window and releases all cached assets
-        virtual void DestroyMainWindow() = 0;
-    };
-    using AtomToolsMainWindowFactoryRequestBus = AZ::EBus<AtomToolsMainWindowFactoryRequests>;
-
-} // namespace AtomToolsFramework

+ 2 - 1
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h

@@ -16,7 +16,8 @@ namespace AtomToolsFramework
     {
     public:
         static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
-        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+        typedef AZ::Crc32 BusIdType;
 
         virtual void OnMainWindowClosing(){};
     };

+ 6 - 3
Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h

@@ -16,12 +16,14 @@ class QWidget;
 
 namespace AtomToolsFramework
 {
-    //! AtomToolsMainWindowRequestBus provides
+    //! AtomToolsMainWindowRequestBus provides an interface to common main application window functions like adding docked windows,
+    //! resizing the viewport, and other operations 
     class AtomToolsMainWindowRequests : public AZ::EBusTraits
     {
     public:
-        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
-        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
+        static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+        static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
+        typedef AZ::Crc32 BusIdType;
 
         //! Bring main window to foreground
         virtual void ActivateWindow() = 0;
@@ -58,6 +60,7 @@ namespace AtomToolsFramework
         //! Releases the viewport's render target resolution lock, allowing it to match the viewport widget again.
         virtual void UnlockViewportRenderTargetSize() {};
     };
+
     using AtomToolsMainWindowRequestBus = AZ::EBus<AtomToolsMainWindowRequests>;
 
 } // namespace AtomToolsFramework

+ 63 - 72
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Application/AtomToolsApplication.cpp

@@ -9,7 +9,6 @@
 #include <Atom/RPI.Public/RPISystemInterface.h>
 #include <AtomToolsFramework/Application/AtomToolsApplication.h>
 #include <AtomToolsFramework/Util/Util.h>
-#include <AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h>
 #include <AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h>
 
 #include <AzCore/Component/ComponentApplicationLifecycle.h>
@@ -46,26 +45,16 @@ AZ_POP_DISABLE_WARNING
 
 namespace AtomToolsFramework
 {
-    AZStd::string AtomToolsApplication::GetBuildTargetName() const
-    {
-        return AZStd::string("AtomTools");
-    }
-
-    const char* AtomToolsApplication::GetCurrentConfigurationName() const
-    {
-#if defined(_RELEASE)
-        return "ReleaseAtomTools";
-#elif defined(_DEBUG)
-        return "DebugAtomTools";
-#else
-        return "ProfileAtomTools";
-#endif
-    }
-
-    AtomToolsApplication::AtomToolsApplication(int* argc, char*** argv)
+    AtomToolsApplication::AtomToolsApplication(const AZStd::string& targetName, int* argc, char*** argv)
         : Application(argc, argv)
         , AzQtApplication(*argc, *argv)
+        , m_targetName(targetName)
+        , m_toolId(targetName)
     {
+        // The settings registry has been created at this point, so add the CMake target
+        AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
+            *AZ::SettingsRegistry::Get(), m_targetName);
+
         // Suppress spam from the Source Control system
         m_traceLogger.AddWindowFilter(AzToolsFramework::SCC_WINDOW);
 
@@ -80,18 +69,26 @@ namespace AtomToolsFramework
         m_styleManager.reset(new AzQtComponents::StyleManager(this));
         m_styleManager->initialize(this, engineRootPath);
 
-        m_timer.setInterval(1);
+        const AZ::u64 updateIntervalWhenActive =
+            GetSettingOrDefault<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenActive", 1);
+        const AZ::u64 updateIntervalWhenNotActive =
+            GetSettingOrDefault<AZ::u64>("/O3DE/AtomToolsFramework/Application/UpdateIntervalWhenNotActive", 64);
+
+        m_timer.setInterval(updateIntervalWhenActive);
         connect(&m_timer, &QTimer::timeout, this, [this]()
         {
             this->PumpSystemEventLoopUntilEmpty();
             this->Tick();
         });
 
-        connect(this, &QGuiApplication::applicationStateChanged, this, [this]()
+        connect(this, &QGuiApplication::applicationStateChanged, this, [this, updateIntervalWhenActive, updateIntervalWhenNotActive]()
         {
             // Limit the update interval when not in focus to reduce power consumption and interference with other applications
-            this->m_timer.setInterval((applicationState() & Qt::ApplicationActive) ? 1 : 32);
+            this->m_timer.setInterval(
+                (applicationState() & Qt::ApplicationActive) ? updateIntervalWhenActive : updateIntervalWhenNotActive);
         });
+
+        AtomToolsMainWindowNotificationBus::Handler::BusConnect(m_toolId);
     }
 
     AtomToolsApplication ::~AtomToolsApplication()
@@ -108,6 +105,17 @@ namespace AtomToolsFramework
         GetSerializeContext()->CreateEditContext();
     }
 
+    const char* AtomToolsApplication::GetCurrentConfigurationName() const
+    {
+#if defined(_RELEASE)
+        return "ReleaseAtomTools";
+#elif defined(_DEBUG)
+        return "DebugAtomTools";
+#else
+        return "ProfileAtomTools";
+#endif
+    }
+
     void AtomToolsApplication::Reflect(AZ::ReflectContext* context)
     {
         Base::Reflect(context);
@@ -181,7 +189,7 @@ namespace AtomToolsFramework
         Base::StartCommon(systemEntity);
 
         const bool clearLogFile = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false);
-        m_traceLogger.OpenLogFile(GetBuildTargetName() + ".log", clearLogFile);
+        m_traceLogger.OpenLogFile(m_targetName + ".log", clearLogFile);
 
         ConnectToAssetProcessor();
 
@@ -200,9 +208,6 @@ namespace AtomToolsFramework
 
         LoadSettings();
 
-        AtomToolsMainWindowNotificationBus::Handler::BusConnect();
-        AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::CreateMainWindow);
-
         auto editorPythonEventsInterface = AZ::Interface<AzToolsFramework::EditorPythonEventsInterface>::Get();
         if (editorPythonEventsInterface)
         {
@@ -218,16 +223,22 @@ namespace AtomToolsFramework
         m_timer.start();
     }
 
-    void AtomToolsApplication::OnMainWindowClosing()
+    void AtomToolsApplication::Tick()
     {
-        ExitMainLoop();
+        TickSystem();
+        Base::Tick();
+
+        if (WasExitMainLoopRequested())
+        {
+            m_timer.disconnect();
+            quit();
+        }
     }
 
     void AtomToolsApplication::Destroy()
     {
-        // before modules are unloaded, destroy UI to free up any assets it cached
-        AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow);
         m_styleManager.reset();
+        UnloadSettings();
 
         AzToolsFramework::EditorPythonConsoleNotificationBus::Handler::BusDisconnect();
         AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusDisconnect();
@@ -241,6 +252,11 @@ namespace AtomToolsFramework
 #endif
     }
 
+    void AtomToolsApplication::OnMainWindowClosing()
+    {
+        ExitMainLoop();
+    }
+
     AZStd::vector<AZStd::string> AtomToolsApplication::GetCriticalAssetFilters() const
     {
         return AZStd::vector<AZStd::string>({});
@@ -255,14 +271,12 @@ namespace AtomToolsFramework
         // and able to negotiate a connection when running a debug build
         // and to negotiate a connection
 
-        const auto targetName = GetBuildTargetName();
-
         AzFramework::AssetSystem::ConnectionSettings connectionSettings;
         AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
         connectionSettings.m_connectionDirection =
             AzFramework::AssetSystem::ConnectionSettings::ConnectionDirection::ConnectToAssetProcessor;
-        connectionSettings.m_connectionIdentifier = targetName;
-        connectionSettings.m_loggingCallback = [targetName]([[maybe_unused]] AZStd::string_view logData)
+        connectionSettings.m_connectionIdentifier = m_targetName;
+        connectionSettings.m_loggingCallback = [targetName = m_targetName]([[maybe_unused]] AZStd::string_view logData)
         {
             AZ_UNUSED(targetName);  // Prevent unused warning in release builds
             AZ_TracePrintf(targetName.c_str(), "%.*s", aznumeric_cast<int>(logData.size()), logData.data());
@@ -279,7 +293,7 @@ namespace AtomToolsFramework
 
     void AtomToolsApplication::CompileCriticalAssets()
     {
-        AZ_TracePrintf(GetBuildTargetName().c_str(), "Compiling critical assets.\n");
+        AZ_TracePrintf(m_targetName.c_str(), "Compiling critical assets.\n");
 
         QStringList failedAssets;
 
@@ -288,7 +302,7 @@ namespace AtomToolsFramework
         // So the asset id won't be found right after CompileAssetSync call.
         for (const AZStd::string& assetFilters : GetCriticalAssetFilters())
         {
-            AZ_TracePrintf(GetBuildTargetName().c_str(), "Compiling critical asset matching: %s.\n", assetFilters.c_str());
+            AZ_TracePrintf(m_targetName.c_str(), "Compiling critical asset matching: %s.\n", assetFilters.c_str());
 
             // Wait for the asset be compiled
             AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
@@ -335,7 +349,7 @@ namespace AtomToolsFramework
             AZ_Assert(context, "No serialize context");
 
             char resolvedPath[AZ_MAX_PATH_LEN] = "";
-            AZStd::string fileName = "@user@/" + GetBuildTargetName() + "UserSettings.xml";
+            AZStd::string fileName = "@user@/" + m_targetName + "UserSettings.xml";
 
             AZ::IO::FileIOBase::GetInstance()->ResolvePath(
                 fileName.c_str(), resolvedPath, AZ_ARRAY_SIZE(resolvedPath));
@@ -350,7 +364,7 @@ namespace AtomToolsFramework
         AZ_Assert(context, "No serialize context");
 
         char resolvedPath[AZ_MAX_PATH_LEN] = "";
-        AZStd::string fileName = "@user@/" + GetBuildTargetName() + "UserSettings.xml";
+        AZStd::string fileName = "@user@/" + m_targetName + "UserSettings.xml";
 
         AZ::IO::FileIOBase::GetInstance()->ResolvePath(fileName.c_str(), resolvedPath, AZ_MAX_PATH_LEN);
 
@@ -376,8 +390,7 @@ namespace AtomToolsFramework
         const AZStd::string activateWindowSwitchName = "activatewindow";
         if (commandLine.HasSwitch(activateWindowSwitchName))
         {
-            AtomToolsFramework::AtomToolsMainWindowRequestBus::Broadcast(
-                &AtomToolsFramework::AtomToolsMainWindowRequestBus::Handler::ActivateWindow);
+            AtomToolsMainWindowRequestBus::Event(m_toolId, &AtomToolsMainWindowRequestBus::Handler::ActivateWindow);
         }
 
         const AZStd::string timeoputSwitchName = "timeout";
@@ -385,14 +398,11 @@ namespace AtomToolsFramework
         {
             const AZStd::string& timeoutValue = commandLine.GetSwitchValue(timeoputSwitchName, 0);
             const uint32_t timeoutInMs = atoi(timeoutValue.c_str());
-            AZ_Printf(GetBuildTargetName().c_str(), "Timeout scheduled, shutting down in %u ms", timeoutInMs);
-            QTimer::singleShot(
-                timeoutInMs,
-                [this]
-                {
-                    AZ_Printf(GetBuildTargetName().c_str(), "Timeout reached, shutting down");
-                    ExitMainLoop();
-                });
+            AZ_Printf(m_targetName.c_str(), "Timeout scheduled, shutting down in %u ms", timeoutInMs);
+            QTimer::singleShot(timeoutInMs, [this] {
+                AZ_Printf(m_targetName.c_str(), "Timeout reached, shutting down");
+                ExitMainLoop();
+            });
         }
 
         // Process command line options for running one or more python scripts on startup
@@ -403,7 +413,7 @@ namespace AtomToolsFramework
             const AZStd::string runPythonScriptPath = commandLine.GetSwitchValue(runPythonScriptSwitchName, runPythonScriptIndex);
             AZStd::vector<AZStd::string_view> runPythonArgs;
 
-            AZ_Printf(GetBuildTargetName().c_str(), "Launching script: %s", runPythonScriptPath.c_str());
+            AZ_Printf(m_targetName.c_str(), "Launching script: %s", runPythonScriptPath.c_str());
             AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
                 &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilenameWithArgs, runPythonScriptPath, runPythonArgs);
         }
@@ -486,27 +496,6 @@ namespace AtomToolsFramework
         return false;
     }
 
-    void AtomToolsApplication::Tick()
-    {
-        TickSystem();
-        Base::Tick();
-
-        if (WasExitMainLoopRequested())
-        {
-            m_timer.disconnect();
-            quit();
-        }
-    }
-
-    void AtomToolsApplication::Stop()
-    {
-        AtomToolsMainWindowFactoryRequestBus::Broadcast(&AtomToolsMainWindowFactoryRequestBus::Handler::DestroyMainWindow);
-        m_styleManager.reset();
-
-        UnloadSettings();
-        Base::Stop();
-    }
-
     void AtomToolsApplication::QueryApplicationType(AZ::ApplicationTypeQuery& appType) const
     {
         appType.m_maskValue = AZ::ApplicationTypeQuery::Masks::Tool;
@@ -524,7 +513,7 @@ namespace AtomToolsFramework
 
         for (auto& line : lines)
         {
-            AZ_TracePrintf(GetBuildTargetName().c_str(), "Python: %s\n", line.c_str());
+            AZ_TracePrintf(m_targetName.c_str(), "Python: %s\n", line.c_str());
         }
 #endif
     }
@@ -537,7 +526,7 @@ namespace AtomToolsFramework
 
     void AtomToolsApplication::OnExceptionMessage([[maybe_unused]] AZStd::string_view message)
     {
-        AZ_Error(GetBuildTargetName().c_str(), false, "Python: " AZ_STRING_FORMAT, AZ_STRING_ARG(message));
+        AZ_Error(m_targetName.c_str(), false, "Python: " AZ_STRING_FORMAT, AZ_STRING_ARG(message));
     }
 
     // Copied from PyIdleWaitFrames in CryEdit.cpp
@@ -577,6 +566,8 @@ namespace AtomToolsFramework
 
     void AtomToolsApplication::PyExit()
     {
-        AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
+        QTimer::singleShot(0, []() { 
+            AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::ExitMainLoop);
+        });
     }
 } // namespace AtomToolsFramework

+ 23 - 18
Gems/Atom/Tools/AtomToolsFramework/Code/Source/AssetBrowser/AtomToolsAssetBrowser.cpp

@@ -89,13 +89,14 @@ namespace AtomToolsFramework
 
     void AtomToolsAssetBrowser::SelectEntries(const AZStd::string& absolutePath)
     {
-        if (!absolutePath.empty())
+        AZ::TickBus::Handler::BusDisconnect();
+
+        m_pathToSelect = absolutePath;
+        if (!m_pathToSelect.empty() && AzFramework::StringFunc::Path::Normalize(m_pathToSelect))
         {
             // Selecting a new asset in the browser is not guaranteed to happen immediately.
             // The asset browser model notifications are sent before the model is updated.
             // Instead of relying on the notifications, queue the selection and process it on tick until this change occurs.
-            m_pathToSelect = absolutePath;
-            AzFramework::StringFunc::Path::Normalize(m_pathToSelect);
             AZ::TickBus::Handler::BusConnect();
         }
     }
@@ -201,25 +202,29 @@ namespace AtomToolsFramework
         AZ_UNUSED(time);
         AZ_UNUSED(deltaTime);
 
-        if (!m_pathToSelect.empty())
+        if (m_pathToSelect.empty())
         {
-            // Attempt to select the new path
-            AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast(
-                &AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Events::SelectFileAtPath, m_pathToSelect);
+            AZ::TickBus::Handler::BusDisconnect();
+            m_pathToSelect.clear();
+            return;
+        }
 
-            // Iterate over the selected entries to verify if the selection was made
-            for (const AssetBrowserEntry* entry : m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets())
+        // Attempt to select the new path
+        AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Broadcast(
+            &AzToolsFramework::AssetBrowser::AssetBrowserViewRequestBus::Events::SelectFileAtPath, m_pathToSelect);
+
+        // Iterate over the selected entries to verify if the selection was made
+        for (const AssetBrowserEntry* entry : m_ui->m_assetBrowserTreeViewWidget->GetSelectedAssets())
+        {
+            if (entry)
             {
-                if (entry)
+                AZStd::string sourcePath = entry->GetFullPath();
+                AzFramework::StringFunc::Path::Normalize(sourcePath);
+                if (m_pathToSelect == sourcePath)
                 {
-                    AZStd::string sourcePath = entry->GetFullPath();
-                    AzFramework::StringFunc::Path::Normalize(sourcePath);
-                    if (m_pathToSelect == sourcePath)
-                    {
-                        // Once the selection is confirmed, cancel the operation and disconnect
-                        AZ::TickBus::Handler::BusDisconnect();
-                        m_pathToSelect.clear();
-                    }
+                    // Once the selection is confirmed, cancel the operation and disconnect
+                    AZ::TickBus::Handler::BusDisconnect();
+                    m_pathToSelect.clear();
                 }
             }
         }

+ 0 - 3
Gems/Atom/Tools/AtomToolsFramework/Code/Source/AtomToolsFrameworkModule.cpp

@@ -8,7 +8,6 @@
 
 #include <AtomToolsFrameworkModule.h>
 #include <AtomToolsFrameworkSystemComponent.h>
-#include <Document/AtomToolsDocumentSystemComponent.h>
 #include <Window/AtomToolsMainWindowSystemComponent.h>
 #include <PerformanceMonitor/PerformanceMonitorSystemComponent.h>
 #include <PreviewRenderer/PreviewRendererSystemComponent.h>
@@ -19,7 +18,6 @@ namespace AtomToolsFramework
     {
         m_descriptors.insert(m_descriptors.end(), {
                 AtomToolsFrameworkSystemComponent::CreateDescriptor(),
-                AtomToolsDocumentSystemComponent::CreateDescriptor(),
                 AtomToolsMainWindowSystemComponent::CreateDescriptor(),
                 PerformanceMonitorSystemComponent::CreateDescriptor(),
                 PreviewRendererSystemComponent::CreateDescriptor(),
@@ -30,7 +28,6 @@ namespace AtomToolsFramework
     {
         return AZ::ComponentTypeList{
             azrtti_typeid<AtomToolsFrameworkSystemComponent>(),
-            azrtti_typeid<AtomToolsDocumentSystemComponent>(),
             azrtti_typeid<AtomToolsMainWindowSystemComponent>(),
             azrtti_typeid<PerformanceMonitorSystemComponent>(),
             azrtti_typeid<PreviewRendererSystemComponent>(),

+ 25 - 24
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocument.cpp

@@ -13,17 +13,18 @@
 
 namespace AtomToolsFramework
 {
-    AtomToolsDocument::AtomToolsDocument()
+    AtomToolsDocument::AtomToolsDocument(const AZ::Crc32& toolId)
+        : m_toolId(toolId)
     {
         AtomToolsDocumentRequestBus::Handler::BusConnect(m_id);
-        AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentCreated, m_id);
+        AtomToolsDocumentNotificationBus::Event(m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentCreated, m_id);
     }
 
     AtomToolsDocument::~AtomToolsDocument()
     {
-        AzToolsFramework::AssetSystemBus::Handler::BusDisconnect();
-        AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentDestroyed, m_id);
+        AtomToolsDocumentNotificationBus::Event(m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentDestroyed, m_id);
         AtomToolsDocumentRequestBus::Handler::BusDisconnect();
+        AzToolsFramework::AssetSystemBus::Handler::BusDisconnect();
     }
 
     const AZ::Uuid& AtomToolsDocument::GetId() const
@@ -80,10 +81,10 @@ namespace AtomToolsFramework
             return false;
         }
 
-        AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id);
-        AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id);
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id);
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id);
         return true;
     }
 
@@ -169,8 +170,8 @@ namespace AtomToolsFramework
 
         AZ_TracePrintf("AtomToolsDocument", "Document closed: '%s'.\n", m_absolutePath.c_str());
 
-        AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentClosed, m_id);
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentClosed, m_id);
 
         // Clearing after notification so paths are still available
         Clear();
@@ -211,8 +212,8 @@ namespace AtomToolsFramework
             // The history index is one beyond the last executed command. Decrement the index then execute undo.
             m_undoHistory[--m_undoHistoryIndex].first();
             AZ_TracePrintf("AtomToolsDocument", "Document undo: '%s'.\n", m_absolutePath.c_str());
-            AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-                &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id);
+            AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+                m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id);
             return true;
         }
         return false;
@@ -225,8 +226,8 @@ namespace AtomToolsFramework
             // Execute the current redo command then move the history index to the next position.
             m_undoHistory[m_undoHistoryIndex++].second();
             AZ_TracePrintf("AtomToolsDocument", "Document redo: '%s'.\n", m_absolutePath.c_str());
-            AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-                &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id);
+            AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+                m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id);
             return true;
         }
         return false;
@@ -259,8 +260,8 @@ namespace AtomToolsFramework
     {
         AZ_TracePrintf("AtomToolsDocument", "Document opened: '%s'.\n", m_absolutePath.c_str());
         AzToolsFramework::AssetSystemBus::Handler::BusConnect();
-        AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, m_id);
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, m_id);
         return true;
     }
 
@@ -282,8 +283,8 @@ namespace AtomToolsFramework
             &AzToolsFramework::SourceControlCommandBus::Events::RequestEdit, m_savePathNormalized.c_str(), true,
             [](bool, const AzToolsFramework::SourceControlFileInfo&) {});
 
-        AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentSaved, m_id);
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentSaved, m_id);
         return true;
     }
 
@@ -319,8 +320,8 @@ namespace AtomToolsFramework
 
         // Assign the index to the end of history
         m_undoHistoryIndex = aznumeric_cast<int>(m_undoHistory.size());
-        AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id);
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentUndoStateChanged, m_id);
     }
 
     void AtomToolsDocument::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, [[maybe_unused]] AZ::Uuid sourceUUID)
@@ -333,16 +334,16 @@ namespace AtomToolsFramework
             if (!m_ignoreSourceFileChangeToSelf)
             {
                 AZ_TracePrintf("AtomToolsDocument", "Document changed externally: '%s'.\n", m_absolutePath.c_str());
-                AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-                    &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id);
+                AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+                    m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentExternallyModified, m_id);
             }
             m_ignoreSourceFileChangeToSelf = false;
         }
         else if (m_sourceDependencies.find(sourcePath) != m_sourceDependencies.end())
         {
             AZ_TracePrintf("AtomToolsDocument", "Document dependency changed: '%s'.\n", m_absolutePath.c_str());
-            AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-                &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id);
+            AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+                m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentDependencyModified, m_id);
         }
     }
 

+ 22 - 4
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentApplication.cpp

@@ -11,11 +11,29 @@
 
 namespace AtomToolsFramework
 {
-    AtomToolsDocumentApplication::AtomToolsDocumentApplication(int* argc, char*** argv)
-        : Base(argc, argv)
+    AtomToolsDocumentApplication::AtomToolsDocumentApplication(const AZStd::string& targetName, int* argc, char*** argv)
+        : Base(targetName, argc, argv)
     {
     }
 
+    void AtomToolsDocumentApplication::Reflect(AZ::ReflectContext* context)
+    {
+        Base::Reflect(context);
+        AtomToolsDocumentSystem::Reflect(context);
+    }
+
+    void AtomToolsDocumentApplication::StartCommon(AZ::Entity* systemEntity)
+    {
+        Base::StartCommon(systemEntity);
+        m_documentSystem.reset(aznew AtomToolsDocumentSystem(m_toolId));
+    }
+
+    void AtomToolsDocumentApplication::Destroy()
+    {
+        m_documentSystem.reset();
+        Base::Destroy();
+    }
+
     void AtomToolsDocumentApplication::ProcessCommandLine(const AZ::CommandLine& commandLine)
     {
         // Process command line options for opening documents on startup
@@ -24,8 +42,8 @@ namespace AtomToolsFramework
         {
             const AZStd::string openDocumentPath = commandLine.GetMiscValue(openDocumentIndex);
 
-            AZ_Printf(GetBuildTargetName().c_str(), "Opening document: %s", openDocumentPath.c_str());
-            AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openDocumentPath);
+            AZ_Printf(m_targetName.c_str(), "Opening document: %s", openDocumentPath.c_str());
+            AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openDocumentPath);
         }
 
         Base::ProcessCommandLine(commandLine);

+ 31 - 23
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentMainWindow.cpp

@@ -25,13 +25,13 @@ AZ_POP_DISABLE_WARNING
 
 namespace AtomToolsFramework
 {
-    AtomToolsDocumentMainWindow::AtomToolsDocumentMainWindow(QWidget* parent /* = 0 */)
-        : AtomToolsMainWindow(parent)
+    AtomToolsDocumentMainWindow::AtomToolsDocumentMainWindow(const AZ::Crc32& toolId, QWidget* parent /* = 0 */)
+        : AtomToolsMainWindow(toolId, parent)
     {
         setObjectName("AtomToolsDocumentMainWindow");
         AddDocumentMenus();
         AddDocumentTabBar();
-        AtomToolsDocumentNotificationBus::Handler::BusConnect();
+        AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
     }
 
     AtomToolsDocumentMainWindow::~AtomToolsDocumentMainWindow()
@@ -49,8 +49,8 @@ namespace AtomToolsFramework
             AZStd::string savePath;
             if (GetCreateDocumentParams(openPath, savePath))
             {
-                AtomToolsDocumentSystemRequestBus::Broadcast(
-                    &AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, openPath, savePath);
+                AtomToolsDocumentSystemRequestBus::Event(
+                    m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile, openPath, savePath);
             }
         }, QKeySequence::New);
         m_menuFile->insertAction(insertPostion, m_actionNew);
@@ -59,7 +59,7 @@ namespace AtomToolsFramework
             AZStd::string openPath;
             if (GetOpenDocumentParams(openPath))
             {
-                AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openPath);
+                AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::OpenDocument, openPath);
             }
         }, QKeySequence::Open);
         m_menuFile->insertAction(insertPostion, m_actionOpen);
@@ -68,7 +68,8 @@ namespace AtomToolsFramework
         m_actionSave = CreateAction("&Save", [this]() {
             const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
             bool result = false;
-            AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocument, documentId);
+            AtomToolsDocumentSystemRequestBus::EventResult(
+                result, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::SaveDocument, documentId);
             if (!result)
             {
                 SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
@@ -81,8 +82,9 @@ namespace AtomToolsFramework
             const QString documentPath = GetDocumentPath(documentId);
 
             bool result = false;
-            AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsCopy,
-                documentId, GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
+            AtomToolsDocumentSystemRequestBus::EventResult(
+                result, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsCopy, documentId,
+                GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
             if (!result)
             {
                 SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
@@ -95,8 +97,9 @@ namespace AtomToolsFramework
             const QString documentPath = GetDocumentPath(documentId);
 
             bool result = false;
-            AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsChild,
-                documentId, GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
+            AtomToolsDocumentSystemRequestBus::EventResult(
+                result, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::SaveDocumentAsChild, documentId,
+                GetSaveFileInfo(documentPath).absoluteFilePath().toUtf8().constData());
             if (!result)
             {
                 SetStatusError(tr("Document save failed: %1").arg(GetDocumentPath(documentId)));
@@ -106,7 +109,8 @@ namespace AtomToolsFramework
 
         m_actionSaveAll = CreateAction("Save A&ll", [this]() {
             bool result = false;
-            AtomToolsDocumentSystemRequestBus::BroadcastResult(result, &AtomToolsDocumentSystemRequestBus::Events::SaveAllDocuments);
+            AtomToolsDocumentSystemRequestBus::EventResult(
+                result, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::SaveAllDocuments);
             if (!result)
             {
                 SetStatusError(tr("Document save all failed"));
@@ -117,18 +121,19 @@ namespace AtomToolsFramework
 
         m_actionClose = CreateAction("&Close", [this]() {
             const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
-            AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
+            AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
         }, QKeySequence::Close);
         m_menuFile->insertAction(insertPostion, m_actionClose);
 
-        m_actionCloseAll = CreateAction("Close All", []() {
-            AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
+        m_actionCloseAll = CreateAction("Close All", [this]() {
+            AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
         });
         m_menuFile->insertAction(insertPostion, m_actionCloseAll);
 
         m_actionCloseOthers = CreateAction("Close Others", [this]() {
             const AZ::Uuid documentId = GetDocumentTabId(m_tabWidget->currentIndex());
-            AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
+            AtomToolsDocumentSystemRequestBus::Event(
+                m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
         });
         m_menuFile->insertAction(insertPostion, m_actionCloseOthers);
         m_menuFile->insertSeparator(insertPostion);
@@ -194,12 +199,12 @@ namespace AtomToolsFramework
         // This should automatically clear the active document
         connect(m_tabWidget, &QTabWidget::currentChanged, this, [this](int tabIndex) {
             const AZ::Uuid documentId = GetDocumentTabId(tabIndex);
-            AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
+            AtomToolsDocumentNotificationBus::Event(m_toolId,&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
         });
 
         connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, [this](int tabIndex) {
             const AZ::Uuid documentId = GetDocumentTabId(tabIndex);
-            AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
+            AtomToolsDocumentSystemRequestBus::Event(m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
         });
 
         // Add context menu for right-clicking on tabs
@@ -341,15 +346,18 @@ namespace AtomToolsFramework
             const QString selectActionName = (currentTabIndex == clickedTabIndex) ? "Select in Browser" : "Select";
             tabMenu.addAction(selectActionName, [this, clickedTabIndex]() {
                 const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex);
-                AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
+                AtomToolsDocumentNotificationBus::Event(
+                    m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
             });
             tabMenu.addAction("Close", [this, clickedTabIndex]() {
                 const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex);
-                AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
+                AtomToolsDocumentSystemRequestBus::Event(
+                    m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
             });
             auto closeOthersAction = tabMenu.addAction("Close Others", [this, clickedTabIndex]() {
                 const AZ::Uuid documentId = GetDocumentTabId(clickedTabIndex);
-                AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
+                AtomToolsDocumentSystemRequestBus::Event(
+                    m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocumentsExcept, documentId);
             });
             closeOthersAction->setEnabled(tabBar->count() > 1);
             tabMenu.exec(QCursor::pos());
@@ -472,14 +480,14 @@ namespace AtomToolsFramework
     void AtomToolsDocumentMainWindow::closeEvent(QCloseEvent* closeEvent)
     {
         bool didClose = true;
-        AtomToolsDocumentSystemRequestBus::BroadcastResult(didClose, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
+        AtomToolsDocumentSystemRequestBus::EventResult(didClose, m_toolId, &AtomToolsDocumentSystemRequestBus::Events::CloseAllDocuments);
         if (!didClose)
         {
             closeEvent->ignore();
             return;
         }
 
-        AtomToolsMainWindowNotificationBus::Broadcast(&AtomToolsMainWindowNotifications::OnMainWindowClosing);
+        AtomToolsMainWindowNotificationBus::Event(m_toolId, &AtomToolsMainWindowNotifications::OnMainWindowClosing);
     }
 
     template<typename Functor>

+ 175 - 173
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystemComponent.cpp → Gems/Atom/Tools/AtomToolsFramework/Code/Source/Document/AtomToolsDocumentSystem.cpp

@@ -9,6 +9,7 @@
 #include <AtomToolsFramework/Debug/TraceRecorder.h>
 #include <AtomToolsFramework/Document/AtomToolsDocumentNotificationBus.h>
 #include <AtomToolsFramework/Document/AtomToolsDocumentRequestBus.h>
+#include <AtomToolsFramework/Document/AtomToolsDocumentSystem.h>
 #include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
 #include <AtomToolsFramework/Util/Util.h>
 #include <AzCore/RTTI/BehaviorContext.h>
@@ -17,7 +18,6 @@
 #include <AzFramework/Asset/AssetSystemBus.h>
 #include <AzFramework/StringFunc/StringFunc.h>
 #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
-#include <Document/AtomToolsDocumentSystemComponent.h>
 
 AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
 #include <QApplication>
@@ -28,20 +28,16 @@ AZ_POP_DISABLE_WARNING
 
 namespace AtomToolsFramework
 {
-    AtomToolsDocumentSystemComponent::AtomToolsDocumentSystemComponent()
+    void AtomToolsDocumentSystem::Reflect(AZ::ReflectContext* context)
     {
-    }
-
-    void AtomToolsDocumentSystemComponent::Reflect(AZ::ReflectContext* context)
-    {
-        if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
+        if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
         {
-            serialize->Class<AtomToolsDocumentSystemComponent, AZ::Component>()
+            serialize->Class<AtomToolsDocumentSystem>()
                 ->Version(0);
 
-            if (AZ::EditContext* ec = serialize->GetEditContext())
+            if (auto editContext = serialize->GetEditContext())
             {
-                ec->Class<AtomToolsDocumentSystemComponent>("AtomToolsDocumentSystemComponent", "")
+                editContext->Class<AtomToolsDocumentSystem>("AtomToolsDocumentSystem", "")
                     ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                     ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("System"))
                     ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
@@ -49,7 +45,7 @@ namespace AtomToolsFramework
             }
         }
 
-        if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
+        if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
         {
             behaviorContext->EBus<AtomToolsDocumentSystemRequestBus>("AtomToolsDocumentSystemRequestBus")
                 ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
@@ -93,40 +89,26 @@ namespace AtomToolsFramework
         }
     }
 
-    void AtomToolsDocumentSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
-    {
-        provided.push_back(AZ_CRC_CE("AtomToolsDocumentSystemService"));
-    }
-
-    void AtomToolsDocumentSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    AtomToolsDocumentSystem::AtomToolsDocumentSystem(const AZ::Crc32& toolId)
+        : m_toolId(toolId)
     {
-        incompatible.push_back(AZ_CRC_CE("AtomToolsDocumentSystemService"));
+        AtomToolsDocumentSystemRequestBus::Handler::BusConnect(m_toolId);
+        AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
     }
 
-    void AtomToolsDocumentSystemComponent::Init()
-    {
-    }
-
-    void AtomToolsDocumentSystemComponent::Activate()
+    AtomToolsDocumentSystem::~AtomToolsDocumentSystem()
     {
         m_documentMap.clear();
-        AtomToolsDocumentSystemRequestBus::Handler::BusConnect();
-        AtomToolsDocumentNotificationBus::Handler::BusConnect();
-    }
-
-    void AtomToolsDocumentSystemComponent::Deactivate()
-    {
         AtomToolsDocumentNotificationBus::Handler::BusDisconnect();
         AtomToolsDocumentSystemRequestBus::Handler::BusDisconnect();
-        m_documentMap.clear();
     }
 
-    void AtomToolsDocumentSystemComponent::RegisterDocumentType(AZStd::function<AtomToolsDocument*()> documentCreator)
+    void AtomToolsDocumentSystem::RegisterDocumentType(const AtomToolsDocumentFactoryCallback& documentCreator)
     {
         m_documentCreator = documentCreator;
     }
 
-    AZ::Uuid AtomToolsDocumentSystemComponent::CreateDocument()
+    AZ::Uuid AtomToolsDocumentSystem::CreateDocument()
     {
         if (!m_documentCreator)
         {
@@ -134,7 +116,7 @@ namespace AtomToolsFramework
             return AZ::Uuid::CreateNull();
         }
 
-        AZStd::unique_ptr<AtomToolsDocument> document(m_documentCreator());
+        AZStd::unique_ptr<AtomToolsDocument> document(m_documentCreator(m_toolId));
         if (!document)
         {
             AZ_Error("AtomToolsDocument", false, "Failed to create new document");
@@ -146,112 +128,17 @@ namespace AtomToolsFramework
         return documentId;
     }
 
-    bool AtomToolsDocumentSystemComponent::DestroyDocument(const AZ::Uuid& documentId)
+    bool AtomToolsDocumentSystem::DestroyDocument(const AZ::Uuid& documentId)
     {
         return m_documentMap.erase(documentId) != 0;
     }
 
-    void AtomToolsDocumentSystemComponent::OnDocumentExternallyModified(const AZ::Uuid& documentId)
-    {
-        m_documentIdsWithExternalChanges.insert(documentId);
-        QueueReopenDocuments();
-    }
-
-    void AtomToolsDocumentSystemComponent::OnDocumentDependencyModified(const AZ::Uuid& documentId)
-    {
-        m_documentIdsWithDependencyChanges.insert(documentId);
-        QueueReopenDocuments();
-    }
-
-    void AtomToolsDocumentSystemComponent::QueueReopenDocuments()
-    {
-        if (!m_queueReopenDocuments)
-        {
-            m_queueReopenDocuments = true;
-            QTimer::singleShot(0, [this] { ReopenDocuments(); });
-        }
-    }
-
-    void AtomToolsDocumentSystemComponent::ReopenDocuments()
-    {
-        const bool enableHotReload = GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/DocumentSystem/EnableHotReload", true);
-        if (!enableHotReload)
-        {
-            m_documentIdsWithDependencyChanges.clear();
-            m_documentIdsWithExternalChanges.clear();
-            m_queueReopenDocuments = false;
-        }
-
-        const bool enableHotReloadPrompts =
-            GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/DocumentSystem/EnableHotReloadPrompts", true);
-
-        for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges)
-        {
-            m_documentIdsWithDependencyChanges.erase(documentId);
-
-            AZStd::string documentPath;
-            AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
-
-            if (enableHotReloadPrompts &&
-                (QMessageBox::question(QApplication::activeWindow(),
-                QString("Document was externally modified"),
-                QString("Would you like to reopen the document:\n%1?").arg(documentPath.c_str()),
-                QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes))
-            {
-                continue;
-            }
-
-            AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
-
-            bool openResult = false;
-            AtomToolsDocumentRequestBus::EventResult(openResult, documentId, &AtomToolsDocumentRequestBus::Events::Open, documentPath);
-            if (!openResult)
-            {
-                QMessageBox::critical(
-                    QApplication::activeWindow(), QString("Document could not be opened"),
-                    QString("Failed to open: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
-                AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
-            }
-        }
-
-        for (const AZ::Uuid& documentId : m_documentIdsWithDependencyChanges)
-        {
-            AZStd::string documentPath;
-            AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
-
-            if (enableHotReloadPrompts &&
-                (QMessageBox::question(QApplication::activeWindow(),
-                QString("Document dependencies have changed"),
-                QString("Would you like to update the document with these changes:\n%1?").arg(documentPath.c_str()),
-                QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes))
-            {
-                continue;
-            }
-
-            AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
-
-            bool openResult = false;
-            AtomToolsDocumentRequestBus::EventResult(openResult, documentId, &AtomToolsDocumentRequestBus::Events::Reopen);
-            if (!openResult)
-            {
-                QMessageBox::critical(
-                    QApplication::activeWindow(), QString("Document could not be opened"),
-                    QString("Failed to open: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
-                AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::CloseDocument, documentId);
-            }
-        }
-
-        m_documentIdsWithDependencyChanges.clear();
-        m_documentIdsWithExternalChanges.clear();
-        m_queueReopenDocuments = false;
-    }
-
-    AZ::Uuid AtomToolsDocumentSystemComponent::OpenDocument(AZStd::string_view sourcePath)
+    AZ::Uuid AtomToolsDocumentSystem::OpenDocument(AZStd::string_view sourcePath)
     {
         return OpenDocumentImpl(sourcePath, true);
     }
 
-    AZ::Uuid AtomToolsDocumentSystemComponent::CreateDocumentFromFile(AZStd::string_view sourcePath, AZStd::string_view targetPath)
+    AZ::Uuid AtomToolsDocumentSystem::CreateDocumentFromFile(AZStd::string_view sourcePath, AZStd::string_view targetPath)
     {
         const AZ::Uuid documentId = OpenDocumentImpl(sourcePath, false);
         if (documentId.IsNull())
@@ -266,18 +153,18 @@ namespace AtomToolsFramework
         }
 
         // Send document open notification after creating new one
-        AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
+        AtomToolsDocumentNotificationBus::Event(m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentId);
         return documentId;
     }
 
-    bool AtomToolsDocumentSystemComponent::CloseDocument(const AZ::Uuid& documentId)
+    bool AtomToolsDocumentSystem::CloseDocument(const AZ::Uuid& documentId)
     {
         bool isOpen = false;
         AtomToolsDocumentRequestBus::EventResult(isOpen, documentId, &AtomToolsDocumentRequestBus::Events::IsOpen);
         if (!isOpen)
         {
             // immediately destroy unopened documents
-            AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::DestroyDocument, documentId);
+            DestroyDocument(documentId);
             return true;
         }
 
@@ -289,8 +176,8 @@ namespace AtomToolsFramework
         if (isModified)
         {
             auto selection = QMessageBox::question(QApplication::activeWindow(),
-                QString("Document has unsaved changes"),
-                QString("Do you want to save changes to\n%1?").arg(documentPath.c_str()),
+                QObject::tr("Document has unsaved changes"),
+                QObject::tr("Do you want to save changes to\n%1?").arg(documentPath.c_str()),
                 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
             if (selection == QMessageBox::Cancel)
             {
@@ -307,23 +194,24 @@ namespace AtomToolsFramework
             }
         }
 
-        AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
+        TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
 
         bool closeResult = true;
         AtomToolsDocumentRequestBus::EventResult(closeResult, documentId, &AtomToolsDocumentRequestBus::Events::Close);
         if (!closeResult)
         {
             QMessageBox::critical(
-                QApplication::activeWindow(), QString("Document could not be closed"),
-                QString("Failed to close: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+                QApplication::activeWindow(),
+                QObject::tr("Document could not be closed"),
+                QObject::tr("Failed to close: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
             return false;
         }
 
-        AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::DestroyDocument, documentId);
+        DestroyDocument(documentId);
         return true;
     }
 
-    bool AtomToolsDocumentSystemComponent::CloseAllDocuments()
+    bool AtomToolsDocumentSystem::CloseAllDocuments()
     {
         bool result = true;
         auto documentMap = m_documentMap;
@@ -338,7 +226,7 @@ namespace AtomToolsFramework
         return result;
     }
 
-    bool AtomToolsDocumentSystemComponent::CloseAllDocumentsExcept(const AZ::Uuid& documentId)
+    bool AtomToolsDocumentSystem::CloseAllDocumentsExcept(const AZ::Uuid& documentId)
     {
         bool result = true;
         auto documentMap = m_documentMap;
@@ -356,7 +244,7 @@ namespace AtomToolsFramework
         return result;
     }
 
-    bool AtomToolsDocumentSystemComponent::SaveDocument(const AZ::Uuid& documentId)
+    bool AtomToolsDocumentSystem::SaveDocument(const AZ::Uuid& documentId)
     {
         AZStd::string saveDocumentPath;
         AtomToolsDocumentRequestBus::EventResult(saveDocumentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
@@ -369,26 +257,30 @@ namespace AtomToolsFramework
         const QFileInfo saveInfo(saveDocumentPath.c_str());
         if (saveInfo.exists() && !saveInfo.isWritable())
         {
-            QMessageBox::critical(QApplication::activeWindow(), "Error", QString("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str()));
+            QMessageBox::critical(
+                QApplication::activeWindow(),
+                QObject::tr("Document could not be saved"),
+                QObject::tr("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str()));
             return false;
         }
 
-        AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
+        TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
 
         bool result = false;
         AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsDocumentRequestBus::Events::Save);
         if (!result)
         {
             QMessageBox::critical(
-                QApplication::activeWindow(), QString("Document could not be saved"),
-                QString("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+                QApplication::activeWindow(),
+                QObject::tr("Document could not be saved"),
+                QObject::tr("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
             return false;
         }
 
         return true;
     }
 
-    bool AtomToolsDocumentSystemComponent::SaveDocumentAsCopy(const AZ::Uuid& documentId, AZStd::string_view targetPath)
+    bool AtomToolsDocumentSystem::SaveDocumentAsCopy(const AZ::Uuid& documentId, AZStd::string_view targetPath)
     {
         AZStd::string saveDocumentPath = targetPath;
         if (saveDocumentPath.empty() || !AzFramework::StringFunc::Path::Normalize(saveDocumentPath))
@@ -399,26 +291,29 @@ namespace AtomToolsFramework
         const QFileInfo saveInfo(saveDocumentPath.c_str());
         if (saveInfo.exists() && !saveInfo.isWritable())
         {
-            QMessageBox::critical(QApplication::activeWindow(), "Error", QString("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str()));
+            QMessageBox::critical(QApplication::activeWindow(),
+                QObject::tr("Document could not be saved"),
+                QObject::tr("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str()));
             return false;
         }
 
-        AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
+        TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
 
         bool result = false;
         AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsDocumentRequestBus::Events::SaveAsCopy, saveDocumentPath);
         if (!result)
         {
             QMessageBox::critical(
-                QApplication::activeWindow(), QString("Document could not be saved"),
-                QString("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+                QApplication::activeWindow(),
+                QObject::tr("Document could not be saved"),
+                QObject::tr("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
             return false;
         }
 
         return true;
     }
 
-    bool AtomToolsDocumentSystemComponent::SaveDocumentAsChild(const AZ::Uuid& documentId, AZStd::string_view targetPath)
+    bool AtomToolsDocumentSystem::SaveDocumentAsChild(const AZ::Uuid& documentId, AZStd::string_view targetPath)
     {
         AZStd::string saveDocumentPath = targetPath;
         if (saveDocumentPath.empty() || !AzFramework::StringFunc::Path::Normalize(saveDocumentPath))
@@ -429,26 +324,30 @@ namespace AtomToolsFramework
         const QFileInfo saveInfo(saveDocumentPath.c_str());
         if (saveInfo.exists() && !saveInfo.isWritable())
         {
-            QMessageBox::critical(QApplication::activeWindow(), "Error", QString("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str()));
+            QMessageBox::critical(
+                QApplication::activeWindow(),
+                QObject::tr("Document could not be saved"),
+                QObject::tr("Document could not be overwritten:\n%1").arg(saveDocumentPath.c_str()));
             return false;
         }
 
-        AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
+        TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
 
         bool result = false;
         AtomToolsDocumentRequestBus::EventResult(result, documentId, &AtomToolsDocumentRequestBus::Events::SaveAsChild, saveDocumentPath);
         if (!result)
         {
             QMessageBox::critical(
-                QApplication::activeWindow(), QString("Document could not be saved"),
-                QString("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+                QApplication::activeWindow(),
+                QObject::tr("Document could not be saved"),
+                QObject::tr("Failed to save: \n%1\n\n%2").arg(saveDocumentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
             return false;
         }
 
         return true;
     }
 
-    bool AtomToolsDocumentSystemComponent::SaveAllDocuments()
+    bool AtomToolsDocumentSystem::SaveAllDocuments()
     {
         bool result = true;
         for (const auto& documentPair : m_documentMap)
@@ -462,12 +361,110 @@ namespace AtomToolsFramework
         return result;
     }
 
-    AZ::u32 AtomToolsDocumentSystemComponent::GetDocumentCount() const
+    AZ::u32 AtomToolsDocumentSystem::GetDocumentCount() const
     {
         return aznumeric_cast<AZ::u32>(m_documentMap.size());
     }
 
-    AZ::Uuid AtomToolsDocumentSystemComponent::OpenDocumentImpl(AZStd::string_view sourcePath, bool checkIfAlreadyOpen)
+
+    void AtomToolsDocumentSystem::OnDocumentExternallyModified(const AZ::Uuid& documentId)
+    {
+        m_documentIdsWithExternalChanges.insert(documentId);
+        QueueReopenDocuments();
+    }
+
+    void AtomToolsDocumentSystem::OnDocumentDependencyModified(const AZ::Uuid& documentId)
+    {
+        m_documentIdsWithDependencyChanges.insert(documentId);
+        QueueReopenDocuments();
+    }
+
+    void AtomToolsDocumentSystem::QueueReopenDocuments()
+    {
+        if (!m_queueReopenDocuments)
+        {
+            m_queueReopenDocuments = true;
+            QTimer::singleShot(0, [this] { ReopenDocuments(); });
+        }
+    }
+
+    void AtomToolsDocumentSystem::ReopenDocuments()
+    {
+        const bool enableHotReload = GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReload", true);
+        if (!enableHotReload)
+        {
+            m_documentIdsWithDependencyChanges.clear();
+            m_documentIdsWithExternalChanges.clear();
+            m_queueReopenDocuments = false;
+        }
+
+        const bool enableHotReloadPrompts =
+            GetSettingOrDefault<bool>("/O3DE/AtomToolsFramework/AtomToolsDocumentSystem/EnableHotReloadPrompts", true);
+
+        for (const AZ::Uuid& documentId : m_documentIdsWithExternalChanges)
+        {
+            m_documentIdsWithDependencyChanges.erase(documentId);
+
+            AZStd::string documentPath;
+            AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
+
+            if (enableHotReloadPrompts &&
+                (QMessageBox::question(QApplication::activeWindow(),
+                QObject::tr("Document was externally modified"),
+                QObject::tr("Would you like to reopen the document:\n%1?").arg(documentPath.c_str()),
+                QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes))
+            {
+                continue;
+            }
+
+            TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
+
+            bool openResult = false;
+            AtomToolsDocumentRequestBus::EventResult(openResult, documentId, &AtomToolsDocumentRequestBus::Events::Open, documentPath);
+            if (!openResult)
+            {
+                QMessageBox::critical(
+                    QApplication::activeWindow(),
+                    QObject::tr("Document could not be opened"),
+                    QObject::tr("Failed to open: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+                CloseDocument(documentId);
+            }
+        }
+
+        for (const AZ::Uuid& documentId : m_documentIdsWithDependencyChanges)
+        {
+            AZStd::string documentPath;
+            AtomToolsDocumentRequestBus::EventResult(documentPath, documentId, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
+
+            if (enableHotReloadPrompts &&
+                (QMessageBox::question(QApplication::activeWindow(),
+                QObject::tr("Document dependencies have changed"),
+                QObject::tr("Would you like to update the document with these changes:\n%1?").arg(documentPath.c_str()),
+                QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes))
+            {
+                continue;
+            }
+
+            TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
+
+            bool openResult = false;
+            AtomToolsDocumentRequestBus::EventResult(openResult, documentId, &AtomToolsDocumentRequestBus::Events::Reopen);
+            if (!openResult)
+            {
+                QMessageBox::critical(
+                    QApplication::activeWindow(),
+                    QObject::tr("Document could not be opened"),
+                    QObject::tr("Failed to open: \n%1\n\n%2").arg(documentPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+                CloseDocument(documentId);
+            }
+        }
+
+        m_documentIdsWithDependencyChanges.clear();
+        m_documentIdsWithExternalChanges.clear();
+        m_queueReopenDocuments = false;
+    }
+
+    AZ::Uuid AtomToolsDocumentSystem::OpenDocumentImpl(AZStd::string_view sourcePath, bool checkIfAlreadyOpen)
     {
         AZStd::string requestedPath = sourcePath;
         if (requestedPath.empty())
@@ -477,7 +474,9 @@ namespace AtomToolsFramework
 
         if (!AzFramework::StringFunc::Path::Normalize(requestedPath))
         {
-            QMessageBox::critical(QApplication::activeWindow(), "Error", QString("Document path is invalid:\n%1").arg(requestedPath.c_str()));
+            QMessageBox::critical(QApplication::activeWindow(),
+                QObject::tr("Document could not be opened"),
+                QObject::tr("Document path is invalid:\n%1").arg(requestedPath.c_str()));
             return AZ::Uuid::CreateNull();
         }
 
@@ -490,21 +489,22 @@ namespace AtomToolsFramework
                 AtomToolsDocumentRequestBus::EventResult(openDocumentPath, documentPair.first, &AtomToolsDocumentRequestBus::Events::GetAbsolutePath);
                 if (openDocumentPath == requestedPath)
                 {
-                    AtomToolsDocumentNotificationBus::Broadcast(&AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentPair.first);
+                    AtomToolsDocumentNotificationBus::Event(
+                        m_toolId, &AtomToolsDocumentNotificationBus::Events::OnDocumentOpened, documentPair.first);
                     return documentPair.first;
                 }
             }
         }
 
-        AtomToolsFramework::TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
+        TraceRecorder traceRecorder(m_maxMessageBoxLineCount);
 
-        AZ::Uuid documentId = AZ::Uuid::CreateNull();
-        AtomToolsDocumentSystemRequestBus::BroadcastResult(documentId, &AtomToolsDocumentSystemRequestBus::Events::CreateDocument);
+        AZ::Uuid documentId = CreateDocument();
         if (documentId.IsNull())
         {
             QMessageBox::critical(
-                QApplication::activeWindow(), QString("Document could not be created"),
-                QString("Failed to create: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+                QApplication::activeWindow(),
+                QObject::tr("Document could not be opened"),
+                QObject::tr("Failed to create: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str()));
             return AZ::Uuid::CreateNull();
         }
 
@@ -513,18 +513,20 @@ namespace AtomToolsFramework
         if (!openResult)
         {
             QMessageBox::critical(
-                QApplication::activeWindow(), QString("Document could not be opened"),
-                QString("Failed to open: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str()));
-            AtomToolsDocumentSystemRequestBus::Broadcast(&AtomToolsDocumentSystemRequestBus::Events::DestroyDocument, documentId);
+                QApplication::activeWindow(),
+                QObject::tr("Document could not be opened"),
+                QObject::tr("Failed to open: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+            DestroyDocument(documentId);
             return AZ::Uuid::CreateNull();
         }
         else if (traceRecorder.GetWarningCount(true) > 0)
         {
             QMessageBox::warning(
-                QApplication::activeWindow(), QString("Document opened with warnings"),
-                QString("Warnings encountered: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str()));
+                QApplication::activeWindow(),
+                QObject::tr("Document opened with warnings"),
+                QObject::tr("Warnings encountered: \n%1\n\n%2").arg(requestedPath.c_str()).arg(traceRecorder.GetDump().c_str()));
         }
 
         return documentId;
     }
-}
+} // namespace AtomToolsFramework

+ 33 - 14
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindow.cpp

@@ -6,8 +6,11 @@
  *
  */
 
+#include <Atom/RHI/Factory.h>
 #include <AtomToolsFramework/PerformanceMonitor/PerformanceMonitorRequestBus.h>
 #include <AtomToolsFramework/Window/AtomToolsMainWindow.h>
+#include <AzCore/Name/Name.h>
+#include <AzCore/Utils/Utils.h>
 #include <AzToolsFramework/API/EditorPythonRunnerRequestsBus.h>
 #include <AzToolsFramework/PythonTerminal/ScriptTermDialog.h>
 
@@ -19,11 +22,11 @@
 
 namespace AtomToolsFramework
 {
-    AtomToolsMainWindow::AtomToolsMainWindow(QWidget* parent)
+    AtomToolsMainWindow::AtomToolsMainWindow(const AZ::Crc32& toolId, QWidget* parent)
         : AzQtComponents::DockMainWindow(parent)
+        , m_toolId(toolId)
+        , m_advancedDockManager(new AzQtComponents::FancyDocking(this))
     {
-        m_advancedDockManager = new AzQtComponents::FancyDocking(this);
-
         setDockNestingEnabled(true);
         setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
         setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
@@ -42,20 +45,21 @@ namespace AtomToolsFramework
         centralWidget->setLayout(centralWidgetLayout);
         setCentralWidget(centralWidget);
 
-        m_assetBrowser = new AtomToolsFramework::AtomToolsAssetBrowser(this);
+        m_assetBrowser = new AtomToolsAssetBrowser(this);
         AddDockWidget("Asset Browser", m_assetBrowser, Qt::BottomDockWidgetArea, Qt::Horizontal);
         AddDockWidget("Python Terminal", new AzToolsFramework::CScriptTermDialog, Qt::BottomDockWidgetArea, Qt::Horizontal);
         SetDockWidgetVisible("Python Terminal", false);
 
         SetupMetrics();
+        UpdateWindowTitle();
+        resize(1280, 1024);
 
-        AtomToolsMainWindowRequestBus::Handler::BusConnect();
+        AtomToolsMainWindowRequestBus::Handler::BusConnect(m_toolId);
     }
 
     AtomToolsMainWindow::~AtomToolsMainWindow()
     {
-        AtomToolsFramework::PerformanceMonitorRequestBus::Broadcast(
-            &AtomToolsFramework::PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, false);
+        PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, false);
         AtomToolsMainWindowRequestBus::Handler::BusDisconnect();
     }
 
@@ -160,10 +164,12 @@ namespace AtomToolsFramework
         m_menuHelp = menuBar()->addMenu("&Help");
 
         m_menuFile->addAction("Run &Python...", [this]() {
-            const QString script = QFileDialog::getOpenFileName(this, "Run Script", QString(), QString("*.py"));
+            const QString script = QFileDialog::getOpenFileName(
+                this, QObject::tr("Run Script"), QString(AZ::Utils::GetProjectPath().c_str()), QString("*.py"));
             if (!script.isEmpty())
             {
-                AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(&AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilename, script.toUtf8().constData());
+                AzToolsFramework::EditorPythonRunnerRequestBus::Broadcast(
+                    &AzToolsFramework::EditorPythonRunnerRequestBus::Events::ExecuteByFilename, script.toUtf8().constData());
             }
         });
 
@@ -213,21 +219,34 @@ namespace AtomToolsFramework
         m_metricsTimer.start();
         connect(&m_metricsTimer, &QTimer::timeout, this, &AtomToolsMainWindow::UpdateMetrics);
 
-        AtomToolsFramework::PerformanceMonitorRequestBus::Broadcast(
-            &AtomToolsFramework::PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, true);
+        PerformanceMonitorRequestBus::Broadcast(&PerformanceMonitorRequestBus::Handler::SetProfilerEnabled, true);
 
         UpdateMetrics();
     }
 
     void AtomToolsMainWindow::UpdateMetrics()
     {
-        AtomToolsFramework::PerformanceMetrics metrics = {};
-        AtomToolsFramework::PerformanceMonitorRequestBus::BroadcastResult(
-            metrics, &AtomToolsFramework::PerformanceMonitorRequestBus::Handler::GetMetrics);
+        PerformanceMetrics metrics = {};
+        PerformanceMonitorRequestBus::BroadcastResult(metrics, &PerformanceMonitorRequestBus::Handler::GetMetrics);
 
         m_statusBarCpuTime->setText(tr("CPU Time %1 ms").arg(QString::number(metrics.m_cpuFrameTimeMs, 'f', 2)));
         m_statusBarGpuTime->setText(tr("GPU Time %1 ms").arg(QString::number(metrics.m_gpuFrameTimeMs, 'f', 2)));
         int frameRate = metrics.m_cpuFrameTimeMs > 0 ? aznumeric_cast<int>(1000 / metrics.m_cpuFrameTimeMs) : 0;
         m_statusBarFps->setText(tr("FPS %1").arg(QString::number(frameRate)));
     }
+
+    void AtomToolsMainWindow::UpdateWindowTitle()
+    {
+        AZ::Name apiName = AZ::RHI::Factory::Get().GetName();
+        if (!apiName.IsEmpty())
+        {
+            QString title = QString{ "%1 (%2)" }.arg(QApplication::applicationName()).arg(apiName.GetCStr());
+            setWindowTitle(title);
+        }
+        else
+        {
+            AZ_Assert(false, "Render API name not found");
+            setWindowTitle(QApplication::applicationName());
+        }
+    }
 } // namespace AtomToolsFramework

+ 0 - 9
Gems/Atom/Tools/AtomToolsFramework/Code/Source/Window/AtomToolsMainWindowSystemComponent.cpp

@@ -6,7 +6,6 @@
  *
  */
 
-#include <AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h>
 #include <AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h>
 #include <AzCore/RTTI/BehaviorContext.h>
 #include <AzCore/Serialization/EditContext.h>
@@ -25,14 +24,6 @@ namespace AtomToolsFramework
 
         if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
         {
-            behaviorContext->EBus<AtomToolsMainWindowFactoryRequestBus>("AtomToolsMainWindowFactoryRequestBus")
-                ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
-                ->Attribute(AZ::Script::Attributes::Category, "Editor")
-                ->Attribute(AZ::Script::Attributes::Module, "atomtools")
-                ->Event("CreateMainWindow", &AtomToolsMainWindowFactoryRequestBus::Events::CreateMainWindow)
-                ->Event("DestroyMainWindow", &AtomToolsMainWindowFactoryRequestBus::Events::DestroyMainWindow)
-                ;
-
             behaviorContext->EBus<AtomToolsMainWindowRequestBus>("AtomToolsMainWindowRequestBus")
                 ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
                 ->Attribute(AZ::Script::Attributes::Category, "Editor")

+ 2 - 3
Gems/Atom/Tools/AtomToolsFramework/Code/atomtoolsframework_files.cmake

@@ -15,6 +15,7 @@ set(FILES
     Include/AtomToolsFramework/Communication/LocalSocket.h
     Include/AtomToolsFramework/Debug/TraceRecorder.h
     Include/AtomToolsFramework/Document/AtomToolsDocument.h
+    Include/AtomToolsFramework/Document/AtomToolsDocumentSystem.h
     Include/AtomToolsFramework/Document/AtomToolsDocumentApplication.h
     Include/AtomToolsFramework/Document/AtomToolsDocumentMainWindow.h
     Include/AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h
@@ -38,7 +39,6 @@ set(FILES
     Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h
     Include/AtomToolsFramework/Window/AtomToolsMainWindow.h
     Include/AtomToolsFramework/Window/AtomToolsMainWindowRequestBus.h
-    Include/AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h
     Include/AtomToolsFramework/Window/AtomToolsMainWindowNotificationBus.h
     Source/Application/AtomToolsApplication.cpp
     Source/AssetBrowser/AtomToolsAssetBrowser.cpp
@@ -53,8 +53,7 @@ set(FILES
     Source/Document/AtomToolsDocument.cpp
     Source/Document/AtomToolsDocumentApplication.cpp
     Source/Document/AtomToolsDocumentMainWindow.cpp
-    Source/Document/AtomToolsDocumentSystemComponent.cpp
-    Source/Document/AtomToolsDocumentSystemComponent.h
+    Source/Document/AtomToolsDocumentSystem.cpp
     Source/DynamicProperty/DynamicProperty.cpp
     Source/DynamicProperty/DynamicPropertyGroup.cpp
     Source/Inspector/InspectorWidget.cpp

+ 8 - 8
Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.cpp

@@ -21,8 +21,8 @@
 
 namespace MaterialEditor
 {
-    MaterialDocument::MaterialDocument()
-        : AtomToolsFramework::AtomToolsDocument()
+    MaterialDocument::MaterialDocument(const AZ::Crc32& toolId)
+        : AtomToolsFramework::AtomToolsDocument(toolId)
     {
         MaterialDocumentRequestBus::Handler::BusConnect(m_id);
     }
@@ -86,12 +86,12 @@ namespace MaterialEditor
                         }
                     }
 
-                    AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-                        &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoChanged, m_id,
+                    AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+                        m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoChanged, m_id,
                         GetObjectInfoFromDynamicPropertyGroup(group.get()), false);
 
-                    AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-                        &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id);
+                    AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+                        m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id);
                     return false;
                 }
             }
@@ -826,8 +826,8 @@ namespace MaterialEditor
 
             if (groupChange || groupRebuilt)
             {
-                AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-                    &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoChanged, m_id,
+                AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+                    m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentObjectInfoChanged, m_id,
                     GetObjectInfoFromDynamicPropertyGroup(group.get()), groupRebuilt);
             }
             return true;

+ 2 - 2
Gems/Atom/Tools/MaterialEditor/Code/Source/Document/MaterialDocument.h

@@ -31,9 +31,9 @@ namespace MaterialEditor
     public:
         AZ_RTTI(MaterialDocument, "{DBA269AE-892B-415C-8FA1-166B94B0E045}");
         AZ_CLASS_ALLOCATOR(MaterialDocument, AZ::SystemAllocator, 0);
-        AZ_DISABLE_COPY(MaterialDocument);
+        AZ_DISABLE_COPY_MOVE(MaterialDocument);
 
-        MaterialDocument();
+        MaterialDocument(const AZ::Crc32& toolId);
         virtual ~MaterialDocument();
 
         // AtomToolsFramework::AtomToolsDocument overrides...

+ 32 - 39
Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.cpp

@@ -42,24 +42,26 @@ void InitMaterialEditorResources()
 
 namespace MaterialEditor
 {
+    static const char* GetBuildTargetName()
+    {
+#if !defined(LY_CMAKE_TARGET)
+#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target"
+#endif
+        return LY_CMAKE_TARGET;
+    }
+
     MaterialEditorApplication::MaterialEditorApplication(int* argc, char*** argv)
-        : Base(argc, argv)
+        : Base(GetBuildTargetName(), argc, argv)
     {
         InitMaterialEditorResources();
 
         QApplication::setApplicationName("O3DE Material Editor");
 
-        // The settings registry has been created at this point, so add the CMake target
-        AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
-            *AZ::SettingsRegistry::Get(), GetBuildTargetName());
-
         AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect();
-        AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusConnect();
     }
 
     MaterialEditorApplication::~MaterialEditorApplication()
     {
-        AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusDisconnect();
         AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect();
         m_window.reset();
     }
@@ -101,35 +103,20 @@ namespace MaterialEditor
     {
         Base::StartCommon(systemEntity);
 
-        AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType,
-            []() { return aznew MaterialDocument(); });
-    }
-
-    AZStd::string MaterialEditorApplication::GetBuildTargetName() const
-    {
-#if !defined(LY_CMAKE_TARGET)
-#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target"
-#endif
-        //! Returns the build system target name of "MaterialEditor"
-        return AZStd::string{ LY_CMAKE_TARGET };
-    }
+        AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType,
+            [](const AZ::Crc32& toolId) { return aznew MaterialDocument(toolId); });
 
-    AZStd::vector<AZStd::string> MaterialEditorApplication::GetCriticalAssetFilters() const
-    {
-        return AZStd::vector<AZStd::string>({ "passes/", "config/", "MaterialEditor/" });
-    }
+        m_window.reset(aznew MaterialEditorWindow(m_toolId));
 
-    void MaterialEditorApplication::CreateMainWindow()
-    {
-        m_window.reset(aznew MaterialEditorWindow);
         m_assetBrowserInteractions.reset(aznew AtomToolsFramework::AtomToolsAssetBrowserInteractions);
+
         m_assetBrowserInteractions->RegisterContextMenuActions(
             [](const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
             {
                 return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source;
             },
-            []([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
+            [this]([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
             {
                 const bool isMaterial = AzFramework::StringFunc::Path::IsExtension(
                     entries.front()->GetFullPath().c_str(), AZ::RPI::MaterialSourceData::Extension);
@@ -137,17 +124,17 @@ namespace MaterialEditor
                     entries.front()->GetFullPath().c_str(), AZ::RPI::MaterialTypeSourceData::Extension);
                 if (isMaterial || isMaterialType)
                 {
-                    menu->addAction(QObject::tr("Open"), [entries]()
+                    menu->addAction(QObject::tr("Open"), [entries, this]()
                         {
-                            AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
-                                &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument,
+                            AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
+                                m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument,
                                 entries.front()->GetFullPath());
                         });
 
                     const QString createActionName =
                         isMaterialType ? QObject::tr("Create Material...") : QObject::tr("Create Child Material...");
 
-                    menu->addAction(createActionName, [entries]()
+                    menu->addAction(createActionName, [entries, this]()
                         {
                             const QString defaultPath = AtomToolsFramework::GetUniqueFileInfo(
                                 QString(AZ::Utils::GetProjectPath().c_str()) +
@@ -155,8 +142,8 @@ namespace MaterialEditor
                                 AZ_CORRECT_FILESYSTEM_SEPARATOR + "untitled." +
                                 AZ::RPI::MaterialSourceData::Extension).absoluteFilePath();
 
-                            AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
-                                &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
+                            AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
+                                m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
                                 entries.front()->GetFullPath(),
                                 AtomToolsFramework::GetSaveFileInfo(defaultPath).absoluteFilePath().toUtf8().constData());
                         });
@@ -175,9 +162,9 @@ namespace MaterialEditor
             {
                 return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Folder;
             },
-            [](QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
+            [this](QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
             {
-                menu->addAction(QObject::tr("Create Material..."), [caller, entries]()
+                menu->addAction(QObject::tr("Create Material..."), [caller, entries, this]()
                     {
                         CreateMaterialDialog createDialog(entries.front()->GetFullPath().c_str(), caller);
                         createDialog.adjustSize();
@@ -186,8 +173,8 @@ namespace MaterialEditor
                             !createDialog.m_materialFileInfo.absoluteFilePath().isEmpty() &&
                             !createDialog.m_materialTypeFileInfo.absoluteFilePath().isEmpty())
                         {
-                            AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
-                                &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
+                            AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
+                                m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::CreateDocumentFromFile,
                                 createDialog.m_materialTypeFileInfo.absoluteFilePath().toUtf8().constData(),
                                 createDialog.m_materialFileInfo.absoluteFilePath().toUtf8().constData());
                         }
@@ -195,9 +182,15 @@ namespace MaterialEditor
             });
     }
 
-    void MaterialEditorApplication::DestroyMainWindow()
+    void MaterialEditorApplication::Destroy()
     {
         m_window.reset();
+        Base::Destroy();
+    }
+
+    AZStd::vector<AZStd::string> MaterialEditorApplication::GetCriticalAssetFilters() const
+    {
+        return AZStd::vector<AZStd::string>({ "passes/", "config/", "MaterialEditor/" });
     }
 
     QWidget* MaterialEditorApplication::GetAppMainWindow()

+ 1 - 7
Gems/Atom/Tools/MaterialEditor/Code/Source/MaterialEditorApplication.h

@@ -10,7 +10,6 @@
 
 #include <AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowserInteractions.h>
 #include <AtomToolsFramework/Document/AtomToolsDocumentApplication.h>
-#include <AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h>
 #include <AzToolsFramework/API/EditorWindowRequestBus.h>
 #include <Window/MaterialEditorWindow.h>
 
@@ -21,7 +20,6 @@ namespace MaterialEditor
     class MaterialEditorApplication
         : public AtomToolsFramework::AtomToolsDocumentApplication
         , private AzToolsFramework::EditorWindowRequestBus::Handler
-        , private AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler
     {
     public:
         AZ_TYPE_INFO(MaterialEditor::MaterialEditorApplication, "{30F90CA5-1253-49B5-8143-19CEE37E22BB}");
@@ -36,15 +34,11 @@ namespace MaterialEditor
         void CreateStaticModules(AZStd::vector<AZ::Module*>& outModules) override;
         const char* GetCurrentConfigurationName() const override;
         void StartCommon(AZ::Entity* systemEntity) override;
+        void Destroy() override;
 
         // AtomToolsFramework::AtomToolsApplication overrides...
-        AZStd::string GetBuildTargetName() const override;
         AZStd::vector<AZStd::string> GetCriticalAssetFilters() const override;
 
-        // AtomToolsMainWindowFactoryRequestBus::Handler overrides...
-        void CreateMainWindow() override;
-        void DestroyMainWindow() override;
-
         // AzToolsFramework::EditorWindowRequests::Bus::Handler
         QWidget* GetAppMainWindow() override;
 

+ 3 - 2
Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.cpp

@@ -60,9 +60,10 @@ namespace MaterialEditor
 {
     static constexpr float DepthNear = 0.01f;
 
-    MaterialViewportWidget::MaterialViewportWidget(QWidget* parent)
+    MaterialViewportWidget::MaterialViewportWidget(const AZ::Crc32& toolId, QWidget* parent)
         : AtomToolsFramework::RenderViewportWidget(parent)
         , m_ui(new Ui::MaterialViewportWidget)
+        , m_toolId(toolId)
         , m_viewportController(AZStd::make_shared<MaterialEditorViewportInputController>())
     {
         m_ui->setupUi(this);
@@ -252,7 +253,7 @@ namespace MaterialEditor
         OnFieldOfViewChanged(viewportSettings->m_fieldOfView);
         OnDisplayMapperOperationTypeChanged(viewportSettings->m_displayMapperOperationType);
 
-        AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect();
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
         MaterialViewportNotificationBus::Handler::BusConnect();
         AZ::TickBus::Handler::BusConnect();
         AZ::TransformNotificationBus::MultiHandler::BusConnect(m_cameraEntity->GetId());

+ 3 - 1
Gems/Atom/Tools/MaterialEditor/Code/Source/Viewport/MaterialViewportWidget.h

@@ -57,7 +57,7 @@ namespace MaterialEditor
         , public AZ::TransformNotificationBus::MultiHandler
     {
     public:
-        MaterialViewportWidget(QWidget* parent = nullptr);
+        MaterialViewportWidget(const AZ::Crc32& toolId, QWidget* parent = nullptr);
         ~MaterialViewportWidget();
 
     private:
@@ -84,6 +84,8 @@ namespace MaterialEditor
         // AZ::TransformNotificationBus::MultiHandler overrides...
         void OnTransformChanged(const AZ::Transform&, const AZ::Transform&) override;
 
+        const AZ::Crc32 m_toolId = {};
+
         using DirectionalLightHandle = AZ::Render::DirectionalLightFeatureProcessorInterface::LightHandle;
 
         AZ::Data::Instance<AZ::RPI::SwapChainPass> m_swapChainPass;

+ 7 - 22
Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.cpp

@@ -6,7 +6,6 @@
  *
  */
 
-#include <Atom/RHI/Factory.h>
 #include <Atom/RPI.Edit/Material/MaterialSourceData.h>
 #include <Atom/RPI.Edit/Material/MaterialTypeSourceData.h>
 #include <AtomToolsFramework/Document/AtomToolsDocumentSystemRequestBus.h>
@@ -36,11 +35,9 @@ AZ_POP_DISABLE_WARNING
 
 namespace MaterialEditor
 {
-    MaterialEditorWindow::MaterialEditorWindow(QWidget* parent /* = 0 */)
-        : Base(parent)
+    MaterialEditorWindow::MaterialEditorWindow(const AZ::Crc32& toolId, QWidget* parent)
+        : Base(toolId, parent)
     {
-        resize(1280, 1024);
-
         // Among other things, we need the window wrapper to save the main window size, position, and state
         auto mainWindowWrapper =
             new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionAutoTitleBarButtons);
@@ -52,36 +49,24 @@ namespace MaterialEditor
 
         QApplication::setWindowIcon(QIcon(":/Icons/materialeditor.svg"));
 
-        AZ::Name apiName = AZ::RHI::Factory::Get().GetName();
-        if (!apiName.IsEmpty())
-        {
-            QString title = QString{ "%1 (%2)" }.arg(QApplication::applicationName()).arg(apiName.GetCStr());
-            setWindowTitle(title);
-        }
-        else
-        {
-            AZ_Assert(false, "Render API name not found");
-            setWindowTitle(QApplication::applicationName());
-        }
-
         setObjectName("MaterialEditorWindow");
 
         m_toolBar = new MaterialEditorToolBar(this);
         m_toolBar->setObjectName("ToolBar");
         addToolBar(m_toolBar);
 
-        m_materialViewport = new MaterialViewportWidget(centralWidget());
+        m_materialViewport = new MaterialViewportWidget(m_toolId, centralWidget());
         m_materialViewport->setObjectName("Viewport");
         m_materialViewport->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
         centralWidget()->layout()->addWidget(m_materialViewport);
 
         m_assetBrowser->SetFilterState("", AZ::RPI::StreamingImageAsset::Group, true);
         m_assetBrowser->SetFilterState("", AZ::RPI::MaterialAsset::Group, true);
-        m_assetBrowser->SetOpenHandler([](const AZStd::string& absolutePath) {
+        m_assetBrowser->SetOpenHandler([this](const AZStd::string& absolutePath) {
             if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::MaterialSourceData::Extension))
             {
-                AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
-                    &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath);
+                AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
+                    m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath);
                 return;
             }
 
@@ -93,7 +78,7 @@ namespace MaterialEditor
             QDesktopServices::openUrl(QUrl::fromLocalFile(absolutePath.c_str()));
         });
 
-        AddDockWidget("Inspector", new MaterialInspector, Qt::RightDockWidgetArea, Qt::Vertical);
+        AddDockWidget("Inspector", new MaterialInspector(m_toolId), Qt::RightDockWidgetArea, Qt::Vertical);
         AddDockWidget("Viewport Settings", new ViewportSettingsInspector, Qt::LeftDockWidgetArea, Qt::Vertical);
         SetDockWidgetVisible("Viewport Settings", false);
 

+ 1 - 1
Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialEditorWindow.h

@@ -32,7 +32,7 @@ namespace MaterialEditor
 
         using Base = AtomToolsFramework::AtomToolsDocumentMainWindow;
 
-        MaterialEditorWindow(QWidget* parent = 0);
+        MaterialEditorWindow(const AZ::Crc32& toolId, QWidget* parent = 0);
 
     protected:
         void ResizeViewportRenderTarget(uint32_t width, uint32_t height) override;

+ 3 - 2
Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.cpp

@@ -15,13 +15,14 @@
 
 namespace MaterialEditor
 {
-    MaterialInspector::MaterialInspector(QWidget* parent)
+    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();
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Handler::BusConnect(m_toolId);
     }
 
     MaterialInspector::~MaterialInspector()

+ 3 - 1
Gems/Atom/Tools/MaterialEditor/Code/Source/Window/MaterialInspector/MaterialInspector.h

@@ -29,7 +29,7 @@ namespace MaterialEditor
     public:
         AZ_CLASS_ALLOCATOR(MaterialInspector, AZ::SystemAllocator, 0);
 
-        explicit MaterialInspector(QWidget* parent = nullptr);
+        MaterialInspector(const AZ::Crc32& toolId, QWidget* parent = nullptr);
         ~MaterialInspector() override;
 
         // AtomToolsFramework::InspectorRequestBus::Handler overrides...
@@ -59,6 +59,8 @@ namespace MaterialEditor
         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 = {};
 

+ 4 - 4
Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.cpp

@@ -14,8 +14,8 @@
 
 namespace ShaderManagementConsole
 {
-    ShaderManagementConsoleDocument::ShaderManagementConsoleDocument()
-        : AtomToolsFramework::AtomToolsDocument()
+    ShaderManagementConsoleDocument::ShaderManagementConsoleDocument(const AZ::Crc32& toolId)
+        : AtomToolsFramework::AtomToolsDocument(toolId)
     {
         ShaderManagementConsoleDocumentRequestBus::Handler::BusConnect(m_id);
     }
@@ -37,8 +37,8 @@ namespace ShaderManagementConsole
             AZ_Error("ShaderManagementConsoleDocument", false, "Could not load shader asset: %s.", shaderPath.c_str());
         }
 
-        AtomToolsFramework::AtomToolsDocumentNotificationBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id);
+        AtomToolsFramework::AtomToolsDocumentNotificationBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentNotificationBus::Events::OnDocumentModified, m_id);
     }
 
     const AZ::RPI::ShaderVariantListSourceData& ShaderManagementConsoleDocument::GetShaderVariantListSourceData() const

+ 3 - 3
Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Document/ShaderManagementConsoleDocument.h

@@ -24,11 +24,11 @@ namespace ShaderManagementConsole
         , public ShaderManagementConsoleDocumentRequestBus::Handler
     {
     public:
-        AZ_RTTI(ShaderManagementConsoleDocument, "{DBA269AE-892B-415C-8FA1-166B94B0E045}");
+        AZ_RTTI(ShaderManagementConsoleDocument, "{504A74BA-F5DD-49E0-BA5E-A381F61DD524}");
         AZ_CLASS_ALLOCATOR(ShaderManagementConsoleDocument, AZ::SystemAllocator, 0);
-        AZ_DISABLE_COPY(ShaderManagementConsoleDocument);
+        AZ_DISABLE_COPY_MOVE(ShaderManagementConsoleDocument);
 
-        ShaderManagementConsoleDocument();
+        ShaderManagementConsoleDocument(const AZ::Crc32& toolId);
         ~ShaderManagementConsoleDocument();
 
         // AtomToolsFramework::AtomToolsDocument overrides...

+ 30 - 37
Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.cpp

@@ -47,27 +47,29 @@ void InitShaderManagementConsoleResources()
 
 namespace ShaderManagementConsole
 {
+    static const char* GetBuildTargetName()
+    {
+#if !defined(LY_CMAKE_TARGET)
+#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target"
+#endif
+        return LY_CMAKE_TARGET;
+    }
+
     ShaderManagementConsoleApplication::ShaderManagementConsoleApplication(int* argc, char*** argv)
-        : Base(argc, argv)
+        : Base(GetBuildTargetName(), argc, argv)
     {
         InitShaderManagementConsoleResources();
 
         QApplication::setApplicationName("O3DE Shader Management Console");
 
-        // The settings registry has been created at this point, so add the CMake target
-        AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddBuildSystemTargetSpecialization(
-            *AZ::SettingsRegistry::Get(), GetBuildTargetName());
-
         ShaderManagementConsoleRequestBus::Handler::BusConnect();
         AzToolsFramework::EditorWindowRequestBus::Handler::BusConnect();
-        AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusConnect();
     }
 
     ShaderManagementConsoleApplication::~ShaderManagementConsoleApplication()
     {
         ShaderManagementConsoleRequestBus::Handler::BusDisconnect();
         AzToolsFramework::EditorWindowRequestBus::Handler::BusDisconnect();
-        AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler::BusDisconnect();
         m_window.reset();
     }
 
@@ -115,40 +117,20 @@ namespace ShaderManagementConsole
     {
         Base::StartCommon(systemEntity);
 
-        AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
-            &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType,
-            []() { return aznew ShaderManagementConsoleDocument(); });
-    }
-
-    AZStd::string ShaderManagementConsoleApplication::GetBuildTargetName() const
-    {
-#if !defined(LY_CMAKE_TARGET)
-#error "LY_CMAKE_TARGET must be defined in order to add this source file to a CMake executable target"
-#endif
-        //! Returns the build system target name of "ShaderManagementConsole"
-        return AZStd::string_view{ LY_CMAKE_TARGET };
-    }
-
-    AZStd::vector<AZStd::string> ShaderManagementConsoleApplication::GetCriticalAssetFilters() const
-    {
-        return AZStd::vector<AZStd::string>({ "passes/", "config/" });
-    }
+        AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
+            m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Handler::RegisterDocumentType,
+            [](const AZ::Crc32& toolId) { return aznew ShaderManagementConsoleDocument(toolId); });
 
-    QWidget* ShaderManagementConsoleApplication::GetAppMainWindow()
-    {
-        return m_window.get();
-    }
+        m_window.reset(aznew ShaderManagementConsoleWindow(m_toolId));
 
-    void ShaderManagementConsoleApplication::CreateMainWindow()
-    {
-        m_window.reset(aznew ShaderManagementConsoleWindow);
         m_assetBrowserInteractions.reset(aznew AtomToolsFramework::AtomToolsAssetBrowserInteractions);
+
         m_assetBrowserInteractions->RegisterContextMenuActions(
             [](const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
             {
                 return entries.front()->GetEntryType() == AzToolsFramework::AssetBrowser::AssetBrowserEntry::AssetEntryType::Source;
             },
-            []([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
+            [this]([[maybe_unused]] QWidget* caller, QMenu* menu, const AtomToolsFramework::AtomToolsAssetBrowserInteractions::AssetBrowserEntryVector& entries)
             {
                 if (AzFramework::StringFunc::Path::IsExtension(
                         entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderSourceData::Extension))
@@ -166,10 +148,10 @@ namespace ShaderManagementConsole
                 else if (AzFramework::StringFunc::Path::IsExtension(
                              entries.front()->GetFullPath().c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
                 {
-                    menu->addAction(QObject::tr("Open"), [entries]()
+                    menu->addAction(QObject::tr("Open"), [entries, this]()
                         {
-                            AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
-                                &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument,
+                            AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
+                                m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument,
                                 entries.front()->GetFullPath());
                         });
                 }
@@ -183,9 +165,20 @@ namespace ShaderManagementConsole
             });
     }
 
-    void ShaderManagementConsoleApplication::DestroyMainWindow()
+    void ShaderManagementConsoleApplication::Destroy()
     {
         m_window.reset();
+        Base::Destroy();
+    }
+
+    AZStd::vector<AZStd::string> ShaderManagementConsoleApplication::GetCriticalAssetFilters() const
+    {
+        return AZStd::vector<AZStd::string>({ "passes/", "config/" });
+    }
+
+    QWidget* ShaderManagementConsoleApplication::GetAppMainWindow()
+    {
+        return m_window.get();
     }
 
     AZ::Data::AssetInfo ShaderManagementConsoleApplication::GetSourceAssetInfo(const AZStd::string& sourceAssetFileName)

+ 1 - 7
Gems/Atom/Tools/ShaderManagementConsole/Code/Source/ShaderManagementConsoleApplication.h

@@ -11,7 +11,6 @@
 #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
 #include <AtomToolsFramework/AssetBrowser/AtomToolsAssetBrowserInteractions.h>
 #include <AtomToolsFramework/Document/AtomToolsDocumentApplication.h>
-#include <AtomToolsFramework/Window/AtomToolsMainWindowFactoryRequestBus.h>
 #include <AzToolsFramework/API/EditorWindowRequestBus.h>
 #include <ShaderManagementConsoleRequestBus.h>
 #include <Window/ShaderManagementConsoleWindow.h>
@@ -21,7 +20,6 @@ namespace ShaderManagementConsole
     class ShaderManagementConsoleApplication
         : public AtomToolsFramework::AtomToolsDocumentApplication
         , private ShaderManagementConsoleRequestBus::Handler
-        , private AtomToolsFramework::AtomToolsMainWindowFactoryRequestBus::Handler
         , private AzToolsFramework::EditorWindowRequestBus::Handler
     {
     public:
@@ -36,18 +34,14 @@ namespace ShaderManagementConsole
         void Reflect(AZ::ReflectContext* context) override;
         const char* GetCurrentConfigurationName() const override;
         void StartCommon(AZ::Entity* systemEntity) override;
+        void Destroy() override;
 
         // AtomToolsFramework::AtomToolsApplication overrides...
-        AZStd::string GetBuildTargetName() const override;
         AZStd::vector<AZStd::string> GetCriticalAssetFilters() const override;
 
         // AzToolsFramework::EditorWindowRequests::Bus::Handler
         QWidget* GetAppMainWindow() override;
 
-        // AtomToolsMainWindowFactoryRequestBus::Handler overrides...
-        void CreateMainWindow() override;
-        void DestroyMainWindow() override;
-
         // ShaderManagementConsoleRequestBus::Handler overrides...
         AZ::Data::AssetInfo GetSourceAssetInfo(const AZStd::string& sourceAssetFileName) override;
         AZStd::vector<AZ::Data::AssetId> FindMaterialAssetsUsingShader(const AZStd::string& shaderFilePath) override;

+ 5 - 7
Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.cpp

@@ -26,11 +26,9 @@ AZ_POP_DISABLE_WARNING
 
 namespace ShaderManagementConsole
 {
-    ShaderManagementConsoleWindow::ShaderManagementConsoleWindow(QWidget* parent /* = 0 */)
-        : Base(parent)
+    ShaderManagementConsoleWindow::ShaderManagementConsoleWindow(const AZ::Crc32& toolId, QWidget* parent)
+        : Base(toolId, parent)
     {
-        resize(1280, 1024);
-
         // Among other things, we need the window wrapper to save the main window size, position, and state
         auto mainWindowWrapper =
             new AzQtComponents::WindowDecorationWrapper(AzQtComponents::WindowDecorationWrapper::OptionAutoTitleBarButtons);
@@ -42,11 +40,11 @@ namespace ShaderManagementConsole
         setObjectName("ShaderManagementConsoleWindow");
 
         m_assetBrowser->SetFilterState("", AZ::RPI::ShaderAsset::Group, true);
-        m_assetBrowser->SetOpenHandler([](const AZStd::string& absolutePath) {
+        m_assetBrowser->SetOpenHandler([this](const AZStd::string& absolutePath) {
             if (AzFramework::StringFunc::Path::IsExtension(absolutePath.c_str(), AZ::RPI::ShaderVariantListSourceData::Extension))
             {
-                AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Broadcast(
-                    &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath);
+                AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Event(
+                    m_toolId, &AtomToolsFramework::AtomToolsDocumentSystemRequestBus::Events::OpenDocument, absolutePath);
                 return;
             }
 

+ 1 - 1
Gems/Atom/Tools/ShaderManagementConsole/Code/Source/Window/ShaderManagementConsoleWindow.h

@@ -31,7 +31,7 @@ namespace ShaderManagementConsole
 
         using Base = AtomToolsFramework::AtomToolsDocumentMainWindow;
 
-        ShaderManagementConsoleWindow(QWidget* parent = 0);
+        ShaderManagementConsoleWindow(const AZ::Crc32& toolId, QWidget* parent = 0);
         ~ShaderManagementConsoleWindow() = default;
 
     protected: