diff --git a/Code/Editor/CryEdit.cpp b/Code/Editor/CryEdit.cpp index 83a98ca67b..ecbdde7f13 100644 --- a/Code/Editor/CryEdit.cpp +++ b/Code/Editor/CryEdit.cpp @@ -728,19 +728,26 @@ void CCryEditApp::OnFileSave() const QScopedValueRollback rollback(m_savingLevel, true); - GetIEditor()->GetDocument()->DoFileSave(); + bool usePrefabSystemForLevels = false; AzFramework::ApplicationRequests::Bus::BroadcastResult( usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled); - if (usePrefabSystemForLevels) + + + if (!usePrefabSystemForLevels) { - auto prefabSystemComponentInterface = AZ::Interface::Get(); - if (prefabSystemComponentInterface->AreDirtyTemplatesPresent()) - { - GetIEditor()->GetDocument()->ExecuteSavePrefabsDialog(); - } + GetIEditor()->GetDocument()->DoFileSave(); + } + else + { + //auto prefabSystemComponentInterface = AZ::Interface::Get(); + auto prefabEditorEntityOwnershipService = AZ::Interface::Get(); + AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId = prefabEditorEntityOwnershipService->GetRootPrefabTemplateId(); + auto prefabIntegrationInterface = AZ::Interface::Get(); + prefabIntegrationInterface->ExecuteSavePrefabsDialog(rootPrefabTemplateId, true); + // prefabSystemComponentInterface->AreDirtyTemplatesPresent(rootPrefabTemplateId) } } @@ -3186,18 +3193,12 @@ bool CCryEditApp::CreateLevel(bool& wasCreateLevelOperationCancelled) } else { - using namespace AzToolsFramework::Prefab; + auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + auto prefabIntegrationInterface = AZ::Interface::Get(); + AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId = prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId(); - auto prefabSystemComponentInterface = AZ::Interface::Get(); - auto prefabSaveSelectionDialog = GetIEditor()->GetDocument()->ConstructSaveLevelDialog(); - - int prefabSaveSelection = prefabSaveSelectionDialog->exec(); - QCheckBox* saveAllPrefabsPreferenceCheckBox = - prefabSaveSelectionDialog->findChild("SaveAllPrefabsPreferenceCheckBox"); - QCheckBox* saveAllPrefabsCheckBox = prefabSaveSelectionDialog->findChild("SaveAllPrefabsCheckbox"); - SavePrefabsPreference savePrefabsPreference = saveAllPrefabsCheckBox->isChecked() - ? SavePrefabsPreference::SaveAll - : SavePrefabsPreference::SaveNone; + int prefabSaveSelection = + prefabIntegrationInterface->ExecuteClosePrefabDialog(rootPrefabTemplateId); // In order to get the accept and reject codes of QDialog and QDialogButtonBox aligned, we do (1-prefabSaveSelection) here. switch (1 - prefabSaveSelection) @@ -3210,16 +3211,7 @@ bool CCryEditApp::CreateLevel(bool& wasCreateLevelOperationCancelled) wasCreateLevelOperationCancelled = true; return false; } - if (saveAllPrefabsPreferenceCheckBox->checkState() == Qt::CheckState::Checked) - { - gSettings.SetSavePrefabsPreference(savePrefabsPreference); - gSettings.Save(); - } - if (savePrefabsPreference == SavePrefabsPreference::SaveAll) - { - prefabSystemComponentInterface->SaveAllDirtyTemplates(); - } - bIsDocModified = prefabSystemComponentInterface->AreDirtyTemplatesPresent(); + bIsDocModified = false; break; case QDialogButtonBox::RejectRole: wasCreateLevelOperationCancelled = true; diff --git a/Code/Editor/CryEditDoc.cpp b/Code/Editor/CryEditDoc.cpp index 3e619aba24..3fb39d4268 100644 --- a/Code/Editor/CryEditDoc.cpp +++ b/Code/Editor/CryEditDoc.cpp @@ -15,8 +15,14 @@ #include #include #include +#include +#include +#include +#include +#include #include #include +#include // AzCore #include @@ -34,9 +40,6 @@ #include #include #include -#include -#include -#include // Editor #include "Settings.h" @@ -138,6 +141,14 @@ CCryEditDoc::CCryEditDoc() RegisterConsoleVariables(); MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_FILE_SAVE_AS, this, &CCryEditDoc::OnFileSaveAs); + m_prefabSystemComponentInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabSystemComponentInterface, "PrefabSystemComponentInterface is not found."); + m_prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabEditorEntityOwnershipInterface, "PrefabEditorEntityOwnershipInterface is not found."); + m_prefabLoaderInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabLoaderInterface, "PrefabLoaderInterface is not found."); + m_prefabIntegrationInterface = AZ::Interface::Get(); + AZ_Assert(m_prefabIntegrationInterface, "PrefabIntegrationInterface is not found."); } CCryEditDoc::~CCryEditDoc() @@ -699,29 +710,18 @@ bool CCryEditDoc::SaveModified() { using namespace AzToolsFramework::Prefab; - auto prefabSystemComponentInterface = AZ::Interface::Get(); - auto prefabSaveSelectionDialog = ConstructSaveLevelDialog(); - - int prefabSaveSelection = prefabSaveSelectionDialog->exec(); - QCheckBox* saveAllPrefabsPreferenceCheckBox = prefabSaveSelectionDialog->findChild("SaveAllPrefabsPreferenceCheckBox"); - QCheckBox* saveAllPrefabsCheckBox = prefabSaveSelectionDialog->findChild("SaveAllPrefabsCheckbox"); - SavePrefabsPreference savePrefabsPreference = - saveAllPrefabsCheckBox->isChecked() ? SavePrefabsPreference::SaveAll : SavePrefabsPreference::SaveNone; + TemplateId rootPrefabTemplateId = m_prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId(); + if (!m_prefabSystemComponentInterface->AreDirtyTemplatesPresent(rootPrefabTemplateId)) + { + return true; + } + + int prefabSaveSelection = m_prefabIntegrationInterface->ExecuteClosePrefabDialog(rootPrefabTemplateId); // In order to get the accept and reject codes of QDialog and QDialogButtonBox aligned, we do (1-prefabSaveSelection) here. switch (1 - prefabSaveSelection) { case QDialogButtonBox::AcceptRole: - DoFileSave(); - if (saveAllPrefabsPreferenceCheckBox->checkState() == Qt::CheckState::Checked) - { - gSettings.SetSavePrefabsPreference(savePrefabsPreference); - gSettings.Save(); - } - if (savePrefabsPreference == SavePrefabsPreference::SaveAll) - { - prefabSystemComponentInterface->SaveAllDirtyTemplates(); - } return true; case QDialogButtonBox::RejectRole: return false; @@ -749,11 +749,9 @@ void CCryEditDoc::OnFileSaveAs() usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled); if (usePrefabSystemForLevels) { - auto prefabSystemComponentInterface = AZ::Interface::Get(); - if (prefabSystemComponentInterface->AreDirtyTemplatesPresent()) - { - ExecuteSavePrefabsDialog(); - } + AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId = + m_prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId(); + SetModifiedFlag(m_prefabSystemComponentInterface->AreDirtyTemplatesPresent(rootPrefabTemplateId)); } } } @@ -1311,8 +1309,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename) } else { - auto prefabEditorEntityOwnershipInterface = AZ::Interface::Get(); - if (prefabEditorEntityOwnershipInterface) + if (m_prefabEditorEntityOwnershipInterface) { AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance(); AZ_Assert(fileIO, "No File IO implementation available"); @@ -1323,7 +1320,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename) if (openResult) { AZ::IO::FileIOStream stream(tempSaveFileHandle, AZ::IO::OpenMode::ModeWrite | AZ::IO::OpenMode::ModeBinary, false); - contentsAllSaved = prefabEditorEntityOwnershipInterface->SaveToStream(stream, AZStd::string_view(filenameStrData.data(), filenameStrData.size())); + contentsAllSaved = m_prefabEditorEntityOwnershipInterface->SaveToStream(stream, AZStd::string_view(filenameStrData.data(), filenameStrData.size())); stream.Close(); } } @@ -2264,196 +2261,6 @@ void CCryEditDoc::OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAsset } ////////////////////////////////////////////////////////////////////////// -AZStd::shared_ptr CCryEditDoc::ConstructSaveLevelDialog() -{ - using namespace AzToolsFramework::Prefab; - auto prefabLoaderInterface = AZ::Interface::Get(); - SavePrefabsPreference savePrefabsPreference = prefabLoaderInterface->GetSavePrefabsPreference(); - - AZStd::shared_ptr saveModifiedMessageBox = AZStd::make_shared(AzToolsFramework::GetActiveWindow()); - AZStd::weak_ptr saveModifiedMessageBoxWeakPtr(saveModifiedMessageBox); - // saveModifiedMessageBox.overrideWindowFlags((saveModifiedMessageBox.windowFlags()) & ~Qt::WindowCloseButtonHint); - saveModifiedMessageBox->setObjectName("SaveDirtyLevelDialog"); - - // Main Content section begins. - QVBoxLayout* contentLayout = new QVBoxLayout(saveModifiedMessageBox.get()); - QFrame* levelEntitiesSaveQuestionFrame = new QFrame(saveModifiedMessageBox.get()); - QHBoxLayout* levelEntitiesSaveQuestionLayout = new QHBoxLayout(saveModifiedMessageBox.get()); - levelEntitiesSaveQuestionFrame->setObjectName("LevelEntitiesSaveQuestionFrame"); - - // Add a warning icon next to save entities question. - levelEntitiesSaveQuestionFrame->setLayout(levelEntitiesSaveQuestionLayout); - QPixmap warningIcon(QString(":/Notifications/warning.svg")); - QLabel* warningIconContainer = new QLabel(); - warningIconContainer->setPixmap(warningIcon); - warningIconContainer->setFixedWidth(warningIcon.width()); - levelEntitiesSaveQuestionLayout->addWidget(warningIconContainer); - - // Ask user if they want to save entities in level. - QLabel* levelEntitiesSaveQuestionLabel = new QLabel("Do you want to save unsaved entities in the level?"); - levelEntitiesSaveQuestionLayout->addWidget(levelEntitiesSaveQuestionLabel); - contentLayout->addWidget(levelEntitiesSaveQuestionFrame); - - // Ask user if they want to save unsaved prefabs in the level too. - QCheckBox* saveAllPrefabsCheckbox = new QCheckBox("Save all unsaved prefabs in the level too."); - AzQtComponents::CheckBox::applyToggleSwitchStyle(saveAllPrefabsCheckbox); - saveAllPrefabsCheckbox->setObjectName("SaveAllPrefabsCheckbox"); - if (savePrefabsPreference == SavePrefabsPreference::SaveAll) - { - saveAllPrefabsCheckbox->setCheckState(Qt::CheckState::Checked); - } - QObject::connect( - saveAllPrefabsCheckbox, &QCheckBox::stateChanged, - [&savePrefabsPreference](int state) - { - savePrefabsPreference = static_cast(state) == Qt::CheckState::Checked ? SavePrefabsPreference::SaveAll - : SavePrefabsPreference::SaveNone; - }); - contentLayout->addWidget(saveAllPrefabsCheckbox); - - // Footer section begins. - QFrame* footerSeparatorLine = new QFrame(); - footerSeparatorLine->setObjectName("FooterSeparatorLine"); - footerSeparatorLine->setFrameShape(QFrame::HLine); - contentLayout->addWidget(footerSeparatorLine); - QHBoxLayout* footerLayout = new QHBoxLayout(saveModifiedMessageBox.get()); - - // Provide option for user to remember their prefab save preference. - QCheckBox* saveAllPrefabsPreferenceCheckBox = new QCheckBox("Remember my preference."); - saveAllPrefabsPreferenceCheckBox->setObjectName("SaveAllPrefabsPreferenceCheckBox"); - AzQtComponents::CheckBox::applyToggleSwitchStyle(saveAllPrefabsPreferenceCheckBox); - if (savePrefabsPreference != SavePrefabsPreference::Unspecified) - { - saveAllPrefabsPreferenceCheckBox->setCheckState(Qt::CheckState::Checked); - } - QVBoxLayout* footerPreferenceLayout = new QVBoxLayout(saveModifiedMessageBox.get()); - footerPreferenceLayout->addWidget(saveAllPrefabsPreferenceCheckBox); - QLabel* prefabSavePreferenceHint = new QLabel("You can change this anytime in Edit -> Editor Settings -> GlobalPreferences."); - prefabSavePreferenceHint->setObjectName("PrefabSavePreferenceHint"); - footerPreferenceLayout->addWidget(prefabSavePreferenceHint); - footerLayout->addLayout(footerPreferenceLayout); - QDialogButtonBox* prefabSaveConfirmationButtons = - new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Discard | QDialogButtonBox::Cancel); - footerLayout->addWidget(prefabSaveConfirmationButtons); - contentLayout->addLayout(footerLayout); - connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, saveModifiedMessageBox.get(), &QDialog::accept); - connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, saveModifiedMessageBox.get(), &QDialog::reject); - connect( - prefabSaveConfirmationButtons, &QDialogButtonBox::clicked, saveModifiedMessageBox.get(), - [saveModifiedMessageBoxWeakPtr, prefabSaveConfirmationButtons](QAbstractButton* button) - { - int prefabSaveSelection = prefabSaveConfirmationButtons->buttonRole(button); - saveModifiedMessageBoxWeakPtr.lock()->done(prefabSaveSelection); - }); - AzQtComponents::StyleManager::setStyleSheet(saveModifiedMessageBox.get(), QStringLiteral("style:Editor.qss")); - return saveModifiedMessageBox; -} - -void CCryEditDoc::ExecuteSavePrefabsDialog() -{ - using namespace AzToolsFramework::Prefab; - - auto prefabSystemComponentInterface = AZ::Interface::Get(); - auto prefabLoaderInterface = AZ::Interface::Get(); - SavePrefabsPreference savePrefabsPreference = prefabLoaderInterface->GetSavePrefabsPreference(); - - if (savePrefabsPreference == SavePrefabsPreference::SaveAll) - { - prefabSystemComponentInterface->SaveAllDirtyTemplates(); - SetModifiedFlag(false); - } - else if (savePrefabsPreference == SavePrefabsPreference::SaveNone) - { - if (prefabSystemComponentInterface->AreDirtyTemplatesPresent()) - { - SetModifiedFlag(true); - } - } - else // SavePrefabsPreference::Unspecified - { - QDialog saveModifiedMessageBox(AzToolsFramework::GetActiveWindow()); - - // Main Content section begins. - saveModifiedMessageBox.setObjectName("SaveAllPrefabsDialog"); - QBoxLayout* contentLayout = new QVBoxLayout(&saveModifiedMessageBox); - QFrame* levelSavedMessageFrame = new QFrame(&saveModifiedMessageBox); - QHBoxLayout* levelSavedMessageLayout = new QHBoxLayout(&saveModifiedMessageBox); - levelSavedMessageFrame->setObjectName("LevelSavedMessageFrame"); - - // Add a checkMark icon next to the level entities saved message. - QPixmap checkMarkIcon(QString(":/Notifications/checkmark.svg")); - QLabel* levelSavedSuccessfullyIconContainer = new QLabel(); - levelSavedSuccessfullyIconContainer->setPixmap(checkMarkIcon); - levelSavedSuccessfullyIconContainer->setFixedWidth(checkMarkIcon.width()); - - // Add a message that level entities are saved successfully. - QLabel* levelSavedSuccessfullyLabel = new QLabel("All entities inside level have been saved successfully."); - levelSavedSuccessfullyLabel->setObjectName("LevelSavedSuccessfullyLabel"); - levelSavedMessageLayout->addWidget(levelSavedSuccessfullyIconContainer); - levelSavedMessageLayout->addWidget(levelSavedSuccessfullyLabel); - levelSavedMessageFrame->setLayout(levelSavedMessageLayout); - - QFrame* prefabSaveQuestionFrame = new QFrame(&saveModifiedMessageBox); - QHBoxLayout* prefabSaveQuestionLayout = new QHBoxLayout(&saveModifiedMessageBox); - - // Add a warning icon next to prefabs save question. - QLabel* warningIconContainer = new QLabel(); - QPixmap warningIcon(QString(":/Notifications/warning.svg")); - warningIconContainer->setPixmap(warningIcon); - warningIconContainer->setFixedWidth(warningIcon.width()); - prefabSaveQuestionLayout->addWidget(warningIconContainer); - - // Ask if user wants all prefabs saved. - QLabel* prefabSaveQuestionLabel = new QLabel("Do you want to save all unsaved prefabs?"); - prefabSaveQuestionFrame->setObjectName("PrefabSaveQuestionFrame"); - prefabSaveQuestionLayout->addWidget(prefabSaveQuestionLabel); - prefabSaveQuestionFrame->setLayout(prefabSaveQuestionLayout); - contentLayout->addWidget(levelSavedMessageFrame); - contentLayout->addWidget(prefabSaveQuestionFrame); - - // Footer section begins. - QFrame* footerSeparatorLine = new QFrame(); - footerSeparatorLine->setObjectName("FooterSeparatorLine"); - footerSeparatorLine->setFrameShape(QFrame::HLine); - contentLayout->addWidget(footerSeparatorLine); - QHBoxLayout* footerLayout = new QHBoxLayout(&saveModifiedMessageBox); - - // Provide option for user to remember their prefab save preference. - QCheckBox* saveAllPrefabsPreference = new QCheckBox("Remember my preference."); - AzQtComponents::CheckBox::applyToggleSwitchStyle(saveAllPrefabsPreference); - QVBoxLayout* footerPreferenceLayout = new QVBoxLayout(&saveModifiedMessageBox); - footerPreferenceLayout->addWidget(saveAllPrefabsPreference); - QLabel* prefabSavePreferenceHint = new QLabel("You can change this anytime in Edit -> Editor Settings -> GlobalPreferences."); - prefabSavePreferenceHint->setObjectName("PrefabSavePreferenceHint"); - footerPreferenceLayout->addWidget(prefabSavePreferenceHint); - footerLayout->addLayout(footerPreferenceLayout); - QDialogButtonBox* prefabSaveConfirmationButtons = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::No); - footerLayout->addWidget(prefabSaveConfirmationButtons); - contentLayout->addLayout(footerLayout); - connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, &saveModifiedMessageBox, &QDialog::accept); - connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, &saveModifiedMessageBox, &QDialog::reject); - AzQtComponents::StyleManager::setStyleSheet(saveModifiedMessageBox.parentWidget(), QStringLiteral("style:Editor.qss")); - - int prefabSaveSelection = saveModifiedMessageBox.exec(); - - if (saveAllPrefabsPreference->checkState() == Qt::CheckState::Checked) - { - gSettings.SetSavePrefabsPreference(savePrefabsPreference); - gSettings.Save(); - } - switch (prefabSaveSelection) - { - case QDialog::Accepted: - prefabSystemComponentInterface->SaveAllDirtyTemplates(); - SetModifiedFlag(false); - break; - case QDialog::Rejected: - SetModifiedFlag(true); - break; - } - } -} - namespace AzToolsFramework { void CryEditDocFuncsHandler::Reflect(AZ::ReflectContext* context) diff --git a/Code/Editor/CryEditDoc.h b/Code/Editor/CryEditDoc.h index 3a12b461fb..b47f4f0645 100644 --- a/Code/Editor/CryEditDoc.h +++ b/Code/Editor/CryEditDoc.h @@ -13,8 +13,13 @@ #if !defined(Q_MOC_RUN) #include "DocMultiArchive.h" +#include #include +#include +#include +#include #include +#include #include #include #endif @@ -104,12 +109,6 @@ public: // Create from serialization only bool CanCloseFrame(); - //! Returns a Modal containing options to save the current level. - AZStd::shared_ptr ConstructSaveLevelDialog(); - - //! Executes a Modal asking users about their prefabs save preference. - void ExecuteSavePrefabsDialog(); - enum class FetchPolicy { DELETE_FOLDER, @@ -215,6 +214,10 @@ protected: const char* m_envProbeSliceRelativePath = "EngineAssets/Slices/DefaultLevelSetup.slice"; const float m_envProbeHeight = 200.0f; bool m_hasErrors = false; ///< This is used to warn the user that they may lose work when they go to save. + AzToolsFramework::Prefab::PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr; + AzToolsFramework::PrefabEditorEntityOwnershipInterface* m_prefabEditorEntityOwnershipInterface = nullptr; + AzToolsFramework::Prefab::PrefabLoaderInterface* m_prefabLoaderInterface = nullptr; + AzToolsFramework::Prefab::PrefabIntegrationInterface* m_prefabIntegrationInterface = nullptr; }; class CAutoDocNotReady diff --git a/Code/Editor/EditorPreferencesPageGeneral.cpp b/Code/Editor/EditorPreferencesPageGeneral.cpp index 9e0d2962a9..4721944b18 100644 --- a/Code/Editor/EditorPreferencesPageGeneral.cpp +++ b/Code/Editor/EditorPreferencesPageGeneral.cpp @@ -42,9 +42,9 @@ void CEditorPreferencesPage_General::Reflect(AZ::SerializeContext& serialize) ->Field("EnableSceneInspector", &GeneralSettings::m_enableSceneInspector) ->Field("RestoreViewportCamera", &GeneralSettings::m_restoreViewportCamera); - serialize.Class() + serialize.Class() ->Version(1) - ->Field("SavePrefabsPreference", &PrefabSettings::m_savePrefabsPreference); + ->Field("SaveAllPrefabsPreference", &GlobalSaveSettings::m_saveAllPrefabsPreference); serialize.Class() ->Version(2) @@ -68,7 +68,7 @@ void CEditorPreferencesPage_General::Reflect(AZ::SerializeContext& serialize) serialize.Class() ->Version(1) ->Field("General Settings", &CEditorPreferencesPage_General::m_generalSettings) - ->Field("Prefab Settings", &CEditorPreferencesPage_General::m_prefabSettings) + ->Field("Global Save Settings", &CEditorPreferencesPage_General::m_globalSaveSettings) ->Field("Messaging", &CEditorPreferencesPage_General::m_messaging) ->Field("Undo", &CEditorPreferencesPage_General::m_undo) ->Field("Deep Selection", &CEditorPreferencesPage_General::m_deepSelection) @@ -97,13 +97,13 @@ void CEditorPreferencesPage_General::Reflect(AZ::SerializeContext& serialize) ->DataElement(AZ::Edit::UIHandlers::CheckBox, &GeneralSettings::m_restoreViewportCamera, EditorPreferencesGeneralRestoreViewportCameraSettingName, "Keep the original editor viewport transform when exiting game mode.") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &GeneralSettings::m_enableSceneInspector, "Enable Scene Inspector (EXPERIMENTAL)", "Enable the option to inspect the internal data loaded from scene files like .fbx. This is an experimental feature. Restart the Scene Settings if the option is not visible under the Help menu."); - editContext->Class("Prefabs", "") + editContext->Class("Global Save Settings (File > Save & Ctrl+S)", "") ->DataElement( - AZ::Edit::UIHandlers::ComboBox, &PrefabSettings::m_savePrefabsPreference, "Save Prefabs Preference", - "When saving levels, this option controls whether and how prefabs should be saved along with the level.") - ->EnumAttribute(AzToolsFramework::Prefab::SavePrefabsPreference::Unspecified, "Unspecified") - ->EnumAttribute(AzToolsFramework::Prefab::SavePrefabsPreference::SaveAll, "Save All") - ->EnumAttribute(AzToolsFramework::Prefab::SavePrefabsPreference::SaveNone, "Save None"); + AZ::Edit::UIHandlers::ComboBox, &GlobalSaveSettings::m_saveAllPrefabsPreference, "Save Prefabs Preference", + "This option controls whether prefabs should be saved along with the level") + ->EnumAttribute(AzToolsFramework::Prefab::SaveAllPrefabsPreference::AskEveryTime, "Ask every time") + ->EnumAttribute(AzToolsFramework::Prefab::SaveAllPrefabsPreference::SaveAll, "Save all") + ->EnumAttribute(AzToolsFramework::Prefab::SaveAllPrefabsPreference::SaveNone, "Save none"); editContext->Class("Messaging", "") ->DataElement(AZ::Edit::UIHandlers::CheckBox, &Messaging::m_showDashboard, "Show Welcome to Open 3D Engine at startup", "Show Welcome to Open 3D Engine at startup") @@ -128,7 +128,7 @@ void CEditorPreferencesPage_General::Reflect(AZ::SerializeContext& serialize) ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20)) ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_General::m_generalSettings, "General Settings", "General Editor Preferences") - ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_General::m_prefabSettings, "Prefabs", "Prefab Settings") + ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_General::m_globalSaveSettings, "Global Save Settings", "Global Save Settings") ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_General::m_messaging, "Messaging", "Messaging") ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_General::m_undo, "Undo", "Undo Preferences") ->DataElement(AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_General::m_deepSelection, "Selection", "Selection") @@ -176,7 +176,7 @@ void CEditorPreferencesPage_General::OnApply() } //prefabs - gSettings.prefabSettings.savePrefabsPreference = m_prefabSettings.m_savePrefabsPreference; + gSettings.globalSaveSettings.saveAllPrefabsPreference = m_globalSaveSettings.m_saveAllPrefabsPreference; //undo gSettings.undoLevels = m_undo.m_undoLevels; @@ -208,7 +208,7 @@ void CEditorPreferencesPage_General::InitializeSettings() m_generalSettings.m_toolbarIconSize = static_cast(gSettings.gui.nToolbarIconSize); //prefabs - m_prefabSettings.m_savePrefabsPreference = gSettings.prefabSettings.savePrefabsPreference; + m_globalSaveSettings.m_saveAllPrefabsPreference = gSettings.globalSaveSettings.saveAllPrefabsPreference; //Messaging m_messaging.m_showDashboard = gSettings.bShowDashboardAtStartup; diff --git a/Code/Editor/EditorPreferencesPageGeneral.h b/Code/Editor/EditorPreferencesPageGeneral.h index b2d652ccba..e5888b2705 100644 --- a/Code/Editor/EditorPreferencesPageGeneral.h +++ b/Code/Editor/EditorPreferencesPageGeneral.h @@ -58,10 +58,10 @@ private: bool m_enableSceneInspector; }; - struct PrefabSettings + struct GlobalSaveSettings { - AZ_TYPE_INFO(PrefabSettings, "{E297DAE3-3985-4BC2-8B43-45F3B1522F6B}"); - AzToolsFramework::Prefab::SavePrefabsPreference m_savePrefabsPreference; + AZ_TYPE_INFO(GlobalSaveSettings, "{E297DAE3-3985-4BC2-8B43-45F3B1522F6B}"); + AzToolsFramework::Prefab::SaveAllPrefabsPreference m_saveAllPrefabsPreference; }; struct Messaging @@ -96,7 +96,7 @@ private: }; GeneralSettings m_generalSettings; - PrefabSettings m_prefabSettings; + GlobalSaveSettings m_globalSaveSettings; Messaging m_messaging; Undo m_undo; DeepSelection m_deepSelection; diff --git a/Code/Editor/Settings.cpp b/Code/Editor/Settings.cpp index d20f132848..4af0989b13 100644 --- a/Code/Editor/Settings.cpp +++ b/Code/Editor/Settings.cpp @@ -255,7 +255,7 @@ SEditorSettings::SEditorSettings() g_TemporaryLevelName = nullptr; sliceSettings.dynamicByDefault = false; - prefabSettings.savePrefabsPreference = AzToolsFramework::Prefab::SavePrefabsPreference::Unspecified; + globalSaveSettings.saveAllPrefabsPreference = AzToolsFramework::Prefab::SaveAllPrefabsPreference::AskEveryTime; } void SEditorSettings::Connect() @@ -672,7 +672,7 @@ void SEditorSettings::Save() AzToolsFramework::Prefab::PrefabLoaderInterface* prefabLoaderInterface = AZ::Interface::Get(); - prefabLoaderInterface->SetSavePrefabsPreference(prefabSettings.savePrefabsPreference); + prefabLoaderInterface->SetSaveAllPrefabsPreference(globalSaveSettings.saveAllPrefabsPreference); SaveSettingsRegistryFile(); } @@ -682,7 +682,7 @@ void SEditorSettings::Load() { AzToolsFramework::Prefab::PrefabLoaderInterface* prefabLoaderInterface = AZ::Interface::Get(); - prefabSettings.savePrefabsPreference = prefabLoaderInterface->GetSavePrefabsPreference(); + globalSaveSettings.saveAllPrefabsPreference = prefabLoaderInterface->GetSaveAllPrefabsPreference(); // Load from Settings Registry AzFramework::ApplicationRequests::Bus::BroadcastResult( @@ -1082,11 +1082,6 @@ void SEditorSettings::ConvertPath(const AZStd::string_view sourcePath, AZStd::st AZStd::replace(category.begin(), category.end(), '|', '\\'); } -void SEditorSettings::SetSavePrefabsPreference(AzToolsFramework::Prefab::SavePrefabsPreference savePrefabsPreference) -{ - prefabSettings.savePrefabsPreference = savePrefabsPreference; -} - AzToolsFramework::EditorSettingsAPIRequests::SettingOutcome SEditorSettings::GetValue(const AZStd::string_view path) { if (path.find("|") == AZStd::string_view::npos) diff --git a/Code/Editor/Settings.h b/Code/Editor/Settings.h index dcc96dd2da..fca3480241 100644 --- a/Code/Editor/Settings.h +++ b/Code/Editor/Settings.h @@ -231,9 +231,9 @@ struct SSliceSettings bool dynamicByDefault; }; -struct SPrefabSettings +struct SGlobalSaveSettings { - AzToolsFramework::Prefab::SavePrefabsPreference savePrefabsPreference; + AzToolsFramework::Prefab::SaveAllPrefabsPreference saveAllPrefabsPreference; }; ////////////////////////////////////////////////////////////////////////// @@ -472,12 +472,10 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING SSliceSettings sliceSettings; - SPrefabSettings prefabSettings; + SGlobalSaveSettings globalSaveSettings; bool prefabSystem = true; ///< Toggle to enable/disable the Prefab system for level entities. - void SetSavePrefabsPreference(AzToolsFramework::Prefab::SavePrefabsPreference savePrefabsPreference); - private: void SaveValue(const char* sSection, const char* sKey, int value); void SaveValue(const char* sSection, const char* sKey, const QColor& value); diff --git a/Code/Editor/Style/Editor.qss b/Code/Editor/Style/Editor.qss index 2999eb388f..72ddeaee5a 100644 --- a/Code/Editor/Style/Editor.qss +++ b/Code/Editor/Style/Editor.qss @@ -245,29 +245,44 @@ QTableWidget#recentLevelTable::item { qproperty-iconSize: 16px 16px; } -#LevelSavedMessageFrame{ +QListWidget::item +{ + border : none; +} + +#SavePrefabDialog, #SaveAllFilesDialog +{ + min-width : 640px; +} + +#SaveDependentPrefabsCard +{ + margin: 0px 15px 10px 15px; +} + +#PrefabSavedMessageFrame{ border: 1px solid green; + margin: 10px 15px 10px 15px; border-radius: 2px; - margin: 5px 20px 5px 20px; padding: 5px 2px 5px 2px; } -#SaveAllPrefabsDialog #PrefabSaveQuestionFrame, #SaveDirtyLevelDialog #LevelEntitiesSaveQuestionFrame, #SaveAllPrefabsCheckbox +#SavePrefabDialog #PrefabSaveWarningFrame { border: 1px solid orange; + margin: 10px 15px 10px 15px; border-radius: 2px; - margin: 5px 20px 5px 20px; padding: 5px 2px 5px 2px; color : white; } -#SaveAllPrefabsDialog #FooterSeparatorLine, #SaveDirtyLevelDialog #FooterSeparatorLine +#SaveAllFilesDialog #FooterSeparatorLine, #SavePrefabDialog #FooterSeparatorLine { color: gray; } -#SaveAllPrefabsDialog #PrefabSavePreferenceHint, #SaveDirtyLevelDialog #PrefabSavePreferenceHint +#SaveAllFilesDialog #PrefabSavePreferenceHint, #SavePrefabDialog #PrefabSavePreferenceHint { font: italic; color: #999999; -} +} \ No newline at end of file diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h index 1cd36e8055..68b9c1dabb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h @@ -43,6 +43,8 @@ namespace AzToolsFramework virtual Prefab::InstanceOptionalReference GetRootPrefabInstance() = 0; + virtual Prefab::TemplateId GetRootPrefabTemplateId() = 0; + //! Get all Assets generated by Prefab processing when entering Play-In Editor mode (Ctrl+G) //! /return The vector of Assets generated by Prefab processing virtual const AZStd::vector>& GetPlayInEditorAssetData() = 0; @@ -54,5 +56,6 @@ namespace AzToolsFramework virtual void StopPlayInEditor() = 0; virtual void CreateNewLevelPrefab(AZStd::string_view filename, const AZStd::string& templateFilename) = 0; + virtual bool IsRootTemplateDirty() = 0; }; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp index 7df1e1b5c1..8994adc9af 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.cpp @@ -359,6 +359,16 @@ namespace AzToolsFramework return AZStd::nullopt; } + Prefab::TemplateId PrefabEditorEntityOwnershipService::GetRootPrefabTemplateId() + { + AZ_Assert(m_rootInstance, "A valid root prefab instance couldn't be found in PrefabEditorEntityOwnershipService."); + if (m_rootInstance) + { + return m_rootInstance->GetTemplateId(); + } + return Prefab::InvalidTemplateId; + } + const AZStd::vector>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData() { return m_playInEditorData.m_assets; @@ -607,6 +617,11 @@ namespace AzToolsFramework m_playInEditorData.m_isEnabled = false; } + bool PrefabEditorEntityOwnershipService::IsRootTemplateDirty() + { + return (m_prefabSystemComponent->IsTemplateDirty(m_rootInstance->GetTemplateId())); + } + ////////////////////////////////////////////////////////////////////////// // Slice Buses implementation with Assert(false), this will exist only during Slice->Prefab // development to pinpoint and replace specific calls to Slice system diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h index bf1199d6dd..962b197b5d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Entity/PrefabEditorEntityOwnershipService.h @@ -193,8 +193,10 @@ namespace AzToolsFramework AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override; Prefab::InstanceOptionalReference GetRootPrefabInstance() override; + Prefab::TemplateId GetRootPrefabTemplateId() override; const AZStd::vector>& GetPlayInEditorAssetData() override; + bool IsRootTemplateDirty() override; ////////////////////////////////////////////////////////////////////////// void OnEntityRemoved(AZ::EntityId entityId); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp index 2f0a0601b9..7b462e3a2f 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp @@ -31,10 +31,10 @@ namespace AzToolsFramework { if (auto* serializeContext = azrtti_cast(context)) { - serializeContext->Enum() - ->Value("Unspecified", SavePrefabsPreference::Unspecified) - ->Value("SaveAll", SavePrefabsPreference::SaveAll) - ->Value("SaveNone", SavePrefabsPreference::SaveNone); + serializeContext->Enum() + ->Value("Ask every time", SaveAllPrefabsPreference::AskEveryTime) + ->Value("Save all", SaveAllPrefabsPreference::SaveAll) + ->Value("Save none", SaveAllPrefabsPreference::SaveNone); } } @@ -669,21 +669,21 @@ namespace AzToolsFramework return finalPath; } - SavePrefabsPreference PrefabLoader::GetSavePrefabsPreference() + SaveAllPrefabsPreference PrefabLoader::GetSaveAllPrefabsPreference() { - SavePrefabsPreference savePrefabsPreference = SavePrefabsPreference::Unspecified; + SaveAllPrefabsPreference saveAllPrefabsPreference = SaveAllPrefabsPreference::AskEveryTime; if (auto* registry = AZ::SettingsRegistry::Get()) { - registry->GetObject(savePrefabsPreference, s_savePrefabsKey); + registry->GetObject(saveAllPrefabsPreference, s_savePrefabsKey); } - return savePrefabsPreference; + return saveAllPrefabsPreference; } - void PrefabLoader::SetSavePrefabsPreference(SavePrefabsPreference savePrefabsPreference) + void PrefabLoader::SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) { if (auto* registry = AZ::SettingsRegistry::Get()) { - registry->SetObject(s_savePrefabsKey, savePrefabsPreference); + registry->SetObject(s_savePrefabsKey, saveAllPrefabsPreference); } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h index e97eb694c2..5940d887da 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.h @@ -110,8 +110,8 @@ namespace AzToolsFramework //! Returns if the path is a valid path for a prefab static bool IsValidPrefabPath(AZ::IO::PathView path); - SavePrefabsPreference GetSavePrefabsPreference() override; - void SetSavePrefabsPreference(SavePrefabsPreference savePrefabsPreference) override; + SaveAllPrefabsPreference GetSaveAllPrefabsPreference() override; + void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) override; private: /** diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h index 57ef6d6a3a..b34f868304 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoaderInterface.h @@ -17,9 +17,9 @@ namespace AzToolsFramework { namespace Prefab { - enum class SavePrefabsPreference + enum class SaveAllPrefabsPreference { - Unspecified, + AskEveryTime, SaveAll, SaveNone }; @@ -91,8 +91,8 @@ namespace AzToolsFramework //! The path will always use the '/' separator. virtual AZ::IO::Path GenerateRelativePath(AZ::IO::PathView path) = 0; - virtual SavePrefabsPreference GetSavePrefabsPreference() = 0; - virtual void SetSavePrefabsPreference(SavePrefabsPreference savePrefabsPreference) = 0; + virtual SaveAllPrefabsPreference GetSaveAllPrefabsPreference() = 0; + virtual void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) = 0; protected: @@ -105,6 +105,6 @@ namespace AzToolsFramework namespace AZ { - AZ_TYPE_INFO_SPECIALIZE(AzToolsFramework::Prefab::SavePrefabsPreference, "{7E61EA82-4DE4-4A3F-945F-C8FEDC1114B5}"); + AZ_TYPE_INFO_SPECIALIZE(AzToolsFramework::Prefab::SaveAllPrefabsPreference, "{7E61EA82-4DE4-4A3F-945F-C8FEDC1114B5}"); } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp index 928158dae0..e465e111aa 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.cpp @@ -754,25 +754,61 @@ namespace AzToolsFramework } } - bool PrefabSystemComponent::AreDirtyTemplatesPresent() + bool PrefabSystemComponent::AreDirtyTemplatesPresent(TemplateId templateId) { - for (const auto& [id, templateObject] : m_templateIdMap) + auto parentTemplate = FindTemplate(templateId); + if (IsTemplateDirty(templateId)) { - if (IsTemplateDirty(id)) + return true; + } + + auto linkIds = parentTemplate->get().GetLinks(); + + for (auto linkId : linkIds) + { + auto linkIterator = m_linkIdMap.find(linkId); + if (linkIterator != m_linkIdMap.end()) { - return true; + return AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId()); } } return false; } - void PrefabSystemComponent::SaveAllDirtyTemplates() + void PrefabSystemComponent::SaveAllDirtyTemplates(TemplateId templateId) { - for (auto& [id, templateObject] : m_templateIdMap) + auto parentTemplate = FindTemplate(templateId); + if (IsTemplateDirty(templateId)) { - if (IsTemplateDirty(id)) + m_prefabLoader.SaveTemplate(templateId); + } + auto linkIds = parentTemplate->get().GetLinks(); + + for (auto linkId : linkIds) + { + auto linkIterator = m_linkIdMap.find(linkId); + if (linkIterator != m_linkIdMap.end()) { - m_prefabLoader.SaveTemplate(id); + SaveAllDirtyTemplates(linkIterator->second.GetSourceTemplateId()); + } + } + } + + void PrefabSystemComponent::GetDirtyTemplatePaths(TemplateId parentTemplateId, AZStd::set& dirtyTemplatePaths) + { + auto parentTemplate = FindTemplate(parentTemplateId); + if (IsTemplateDirty(parentTemplateId)) + { + dirtyTemplatePaths.emplace(parentTemplate->get().GetFilePath()); + } + auto linkIds = parentTemplate->get().GetLinks(); + + for (auto linkId : linkIds) + { + auto linkIterator = m_linkIdMap.find(linkId); + if (linkIterator != m_linkIdMap.end()) + { + GetDirtyTemplatePaths(linkIterator->second.GetSourceTemplateId(), dirtyTemplatePaths); } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h index cb95f9b367..153a0e2b47 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponent.h @@ -183,9 +183,11 @@ namespace AzToolsFramework */ void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) override; - bool AreDirtyTemplatesPresent() override; + bool AreDirtyTemplatesPresent(TemplateId templateId) override; - void SaveAllDirtyTemplates() override; + void SaveAllDirtyTemplates(TemplateId templateId) override; + + void GetDirtyTemplatePaths(TemplateId parentTemplateId, AZStd::set& dirtyTemplatePaths) override; ////////////////////////////////////////////////////////////////////////// diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h index 252c8bdb6a..a5abf138f7 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemComponentInterface.h @@ -9,11 +9,12 @@ #pragma once #include +#include +#include #include #include #include #include -#include namespace AzToolsFramework { @@ -49,8 +50,9 @@ namespace AzToolsFramework virtual bool IsTemplateDirty(const TemplateId& templateId) = 0; virtual void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) = 0; - virtual bool AreDirtyTemplatesPresent() = 0; - virtual void SaveAllDirtyTemplates() = 0; + virtual bool AreDirtyTemplatesPresent(TemplateId templateId) = 0; + virtual void SaveAllDirtyTemplates(TemplateId templateId) = 0; + virtual void GetDirtyTemplatePaths(TemplateId parentTemplateId, AZStd::set& dirtyTemplatePaths) = 0; virtual PrefabDom& FindTemplateDom(TemplateId templateId) = 0; virtual void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) = 0; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h index 0befe55822..afd764232d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace AzToolsFramework { @@ -28,6 +29,9 @@ namespace AzToolsFramework * @return The id of the newly created entity. */ virtual AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) = 0; + + virtual int ExecuteClosePrefabDialog(TemplateId templateId) = 0; + virtual void ExecuteSavePrefabsDialog(TemplateId templateId, bool useSaveAllPrefabsPreference = false) = 0; }; } // namespace Prefab diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp index c17b96411e..825dea6072 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -27,12 +28,28 @@ #include #include +#include +#include +#include +#include + + #include +#include +#include +#include #include #include +#include +#include +#include #include #include #include +#include +#include +#include + namespace AzToolsFramework { @@ -43,6 +60,7 @@ namespace AzToolsFramework PrefabPublicInterface* PrefabIntegrationManager::s_prefabPublicInterface = nullptr; PrefabEditInterface* PrefabIntegrationManager::s_prefabEditInterface = nullptr; PrefabLoaderInterface* PrefabIntegrationManager::s_prefabLoaderInterface = nullptr; + PrefabSystemComponentInterface* PrefabIntegrationManager::s_prefabSystemComponentInterface = nullptr; const AZStd::string PrefabIntegrationManager::s_prefabFileExtension = ".prefab"; @@ -88,6 +106,13 @@ namespace AzToolsFramework return; } + s_prefabSystemComponentInterface = AZ::Interface::Get(); + if (s_prefabSystemComponentInterface == nullptr) + { + AZ_Assert(false, "Prefab - could not get PrefabSystemComponentInterface on PrefabIntegrationManager construction."); + return; + } + EditorContextMenuBus::Handler::BusConnect(); PrefabInstanceContainerNotificationBus::Handler::BusConnect(); AZ::Interface::Register(this); @@ -1050,5 +1075,232 @@ namespace AzToolsFramework return AZ::EntityId(); } } + + int PrefabIntegrationManager::ExecuteClosePrefabDialog(TemplateId templateId) + { + auto prefabSaveSelectionDialog = ConstructClosePrefabDialog(templateId); + + int prefabSaveSelection = prefabSaveSelectionDialog->exec(); + + if (prefabSaveSelection == QDialog::Accepted) + { + SavePrefabsInDialog(prefabSaveSelectionDialog.get()); + } + return prefabSaveSelection; + } + + void PrefabIntegrationManager::ExecuteSavePrefabsDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) + { + using namespace AzToolsFramework::Prefab; + + auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId); + AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath(); + + if (s_prefabSystemComponentInterface->IsTemplateDirty(templateId)) + { + if (s_prefabLoaderInterface->SaveTemplate(templateId) == false) + { + AZ_Error("Prefabs", false, "Template '%s' could not be saved successfully.", prefabTemplatePath.c_str()); + return; + } + } + + if (useSaveAllPrefabsPreference) + { + SaveAllPrefabsPreference saveAllPrefabsPreference = s_prefabLoaderInterface->GetSaveAllPrefabsPreference(); + + if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveAll) + { + s_prefabSystemComponentInterface->SaveAllDirtyTemplates(templateId); + return; + } + else if (saveAllPrefabsPreference == SaveAllPrefabsPreference::SaveNone) + { + return; + } + } + + AZStd::unique_ptr savePrefabsDialog = ConstructSavePrefabsDialog(templateId, useSaveAllPrefabsPreference); + if (savePrefabsDialog) + { + int prefabSaveSelection = savePrefabsDialog->exec(); + + if (prefabSaveSelection == QDialog::Accepted) + { + SavePrefabsInDialog(savePrefabsDialog.get()); + } + } + } + + void PrefabIntegrationManager::SavePrefabsInDialog(QDialog* unsavedPrefabsDialog) + { + QList unsavedPrefabFileLabels = unsavedPrefabsDialog->findChildren("UnsavedPrefabFileName"); + if (unsavedPrefabFileLabels.size() > 0) + { + for (const QLabel* unsavedPrefabFileLabel : unsavedPrefabFileLabels) + { + AZStd::string unsavedPrefabFileName = unsavedPrefabFileLabel->property("FilePath").toString().toUtf8().data(); + AzToolsFramework::Prefab::TemplateId unsavedPrefabTemplateId = + s_prefabSystemComponentInterface->GetTemplateIdFromFilePath(unsavedPrefabFileName.data()); + bool isTemplateSavedSuccessfully = s_prefabLoaderInterface->SaveTemplate(unsavedPrefabTemplateId); + AZ_Assert(isTemplateSavedSuccessfully, "Prefab '%s' could not be saved successfully.", unsavedPrefabFileName.c_str()); + } + } + } + + AZStd::unique_ptr PrefabIntegrationManager::ConstructSavePrefabsDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) + { + AZStd::unique_ptr saveModifiedMessageBox = AZStd::make_unique(AzToolsFramework::GetActiveWindow()); + + saveModifiedMessageBox->setWindowTitle("Unsaved files detected"); + + // Main Content section begins. + saveModifiedMessageBox->setObjectName("SaveAllFilesDialog"); + QBoxLayout* contentLayout = new QVBoxLayout(saveModifiedMessageBox.get()); + + QFrame* prefabSavedMessageFrame = new QFrame(saveModifiedMessageBox.get()); + QHBoxLayout* prefabSavedMessageLayout = new QHBoxLayout(saveModifiedMessageBox.get()); + prefabSavedMessageFrame->setObjectName("PrefabSavedMessageFrame"); + prefabSavedMessageFrame->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + + // Add a checkMark icon next to the level entities saved message. + QPixmap checkMarkIcon(QString(":/Notifications/checkmark.svg")); + QLabel* prefabSavedSuccessfullyIconContainer = new QLabel(); + prefabSavedSuccessfullyIconContainer->setPixmap(checkMarkIcon); + prefabSavedSuccessfullyIconContainer->setFixedWidth(checkMarkIcon.width()); + + // Add a message that level entities are saved successfully. + + auto prefabTemplate = s_prefabSystemComponentInterface->FindTemplate(templateId); + AZ::IO::Path prefabTemplatePath = prefabTemplate->get().GetFilePath(); + QLabel* prefabSavedSuccessfullyLabel = new QLabel( + QString("Prefab %1 has been saved. Do you want to save the below dependent prefabs too?").arg(prefabTemplatePath.c_str())); + prefabSavedSuccessfullyLabel->setObjectName("PrefabSavedSuccessfullyLabel"); + prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyIconContainer); + prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyLabel); + prefabSavedMessageFrame->setLayout(prefabSavedMessageLayout); + contentLayout->addWidget(prefabSavedMessageFrame); + + AzQtComponents::Card* unsavedPrefabsContainer = ConstructUnsavedPrefabsCard(templateId); + contentLayout->addWidget(unsavedPrefabsContainer); + + contentLayout->addStretch(); + + // Footer section begins. + QHBoxLayout* footerLayout = new QHBoxLayout(saveModifiedMessageBox.get()); + + if (useSaveAllPrefabsPreference) + { + QFrame* footerSeparatorLine = new QFrame(); + footerSeparatorLine->setObjectName("FooterSeparatorLine"); + footerSeparatorLine->setFrameShape(QFrame::HLine); + contentLayout->addWidget(footerSeparatorLine); + + QLabel* prefabSavePreferenceHint = + new QLabel("You can prevent this window from showing in the future by updating your global save preferences."); + prefabSavePreferenceHint->setToolTip( + "Go to 'Edit > Editor Settings > Global Preferences... > Global save preferences' to update your preference"); + prefabSavePreferenceHint->setObjectName("PrefabSavePreferenceHint"); + footerLayout->addWidget(prefabSavePreferenceHint); + } + + QDialogButtonBox* prefabSaveConfirmationButtons = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::No); + footerLayout->addWidget(prefabSaveConfirmationButtons); + contentLayout->addLayout(footerLayout); + connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, saveModifiedMessageBox.get(), &QDialog::accept); + connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, saveModifiedMessageBox.get(), &QDialog::reject); + AzQtComponents::StyleManager::setStyleSheet(saveModifiedMessageBox->parentWidget(), QStringLiteral("style:Editor.qss")); + + return AZStd::move(saveModifiedMessageBox); + } + + AZStd::shared_ptr PrefabIntegrationManager::ConstructClosePrefabDialog(TemplateId templateId) + { + AZStd::shared_ptr saveModifiedMessageBox = AZStd::make_shared(AzToolsFramework::GetActiveWindow()); + saveModifiedMessageBox->setWindowTitle("Unsaved files detected"); + AZStd::weak_ptr saveModifiedMessageBoxWeakPtr(saveModifiedMessageBox); + saveModifiedMessageBox->setObjectName("SavePrefabDialog"); + + // Main Content section begins. + QVBoxLayout* contentLayout = new QVBoxLayout(saveModifiedMessageBox.get()); + QFrame* prefabSaveWarningFrame = new QFrame(saveModifiedMessageBox.get()); + QHBoxLayout* levelEntitiesSaveQuestionLayout = new QHBoxLayout(saveModifiedMessageBox.get()); + prefabSaveWarningFrame->setObjectName("PrefabSaveWarningFrame"); + + // Add a warning icon next to save prefab warning. + prefabSaveWarningFrame->setLayout(levelEntitiesSaveQuestionLayout); + QPixmap warningIcon(QString(":/Notifications/warning.svg")); + QLabel* warningIconContainer = new QLabel(); + warningIconContainer->setPixmap(warningIcon); + warningIconContainer->setFixedWidth(warningIcon.width()); + levelEntitiesSaveQuestionLayout->addWidget(warningIconContainer); + + // Ask user if they want to save entities in level. + QLabel* prefabSaveQuestionLabel = new QLabel("Do you want to save the below unsaved prefabs?", saveModifiedMessageBox.get()); + levelEntitiesSaveQuestionLayout->addWidget(prefabSaveQuestionLabel); + contentLayout->addWidget(prefabSaveWarningFrame); + + AZStd::set dirtyTemplatePaths; + s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId, dirtyTemplatePaths); + auto templateToSave = s_prefabSystemComponentInterface->FindTemplate(templateId); + AZ::IO::Path templateToSaveFilePath = templateToSave->get().GetFilePath(); + AzQtComponents::Card* unsavedPrefabsCard = ConstructUnsavedPrefabsCard(templateId); + contentLayout->addWidget(unsavedPrefabsCard); + + contentLayout->addStretch(); + + QHBoxLayout* footerLayout = new QHBoxLayout(saveModifiedMessageBox.get()); + + QDialogButtonBox* prefabSaveConfirmationButtons = + new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Discard | QDialogButtonBox::Cancel); + footerLayout->addWidget(prefabSaveConfirmationButtons); + contentLayout->addLayout(footerLayout); + QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, saveModifiedMessageBox.get(), &QDialog::accept); + QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, saveModifiedMessageBox.get(), &QDialog::reject); + QObject::connect( + prefabSaveConfirmationButtons, &QDialogButtonBox::clicked, saveModifiedMessageBox.get(), + [saveModifiedMessageBoxWeakPtr, prefabSaveConfirmationButtons](QAbstractButton* button) + { + int prefabSaveSelection = prefabSaveConfirmationButtons->buttonRole(button); + saveModifiedMessageBoxWeakPtr.lock()->done(prefabSaveSelection); + }); + AzQtComponents::StyleManager::setStyleSheet(saveModifiedMessageBox.get(), QStringLiteral("style:Editor.qss")); + return saveModifiedMessageBox; + } + + AzQtComponents::Card* PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId) + { + FlowLayout* unsavedPrefabsLayout = new FlowLayout; + + AZStd::set dirtyTemplatePaths; + s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId, dirtyTemplatePaths); + + for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths) + { + QLabel* prefabNameLabel = new QLabel(QString("%1").arg(dirtyTemplatePath.Filename().Native().data())); + prefabNameLabel->setObjectName("UnsavedPrefabFileName"); + prefabNameLabel->setWordWrap(true); + prefabNameLabel->setToolTip(dirtyTemplatePath.Native().data()); + prefabNameLabel->setProperty("FilePath", dirtyTemplatePath.Native().data()); + unsavedPrefabsLayout->addWidget(prefabNameLabel); + } + + AzQtComponents::Card* unsavedPrefabsContainer = new AzQtComponents::Card; + unsavedPrefabsContainer->setObjectName("SaveDependentPrefabsCard"); + unsavedPrefabsContainer->setTitle("Unsaved Prefabs"); + unsavedPrefabsContainer->header()->setHasContextMenu(false); + unsavedPrefabsContainer->header()->setIcon(QIcon(QStringLiteral(":/Entity/prefab_edit.svg"))); + + QFrame* unsavedPrefabsFrame = new QFrame(unsavedPrefabsContainer); + unsavedPrefabsFrame->setLayout(unsavedPrefabsLayout); + QScrollArea* unsavedPrefabsScrollArea = new QScrollArea(); + unsavedPrefabsScrollArea->setWidget(unsavedPrefabsFrame); + //unsavedPrefabsScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + unsavedPrefabsScrollArea->setWidgetResizable(true); + unsavedPrefabsScrollArea->setObjectName("SavePrefabsCardContent"); + unsavedPrefabsContainer->setContentWidget(unsavedPrefabsScrollArea); + + return AZStd::move(unsavedPrefabsContainer); + } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h index b40f0169c9..8a210c2de0 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/UI/Prefab/PrefabIntegrationManager.h @@ -15,12 +15,15 @@ #include #include #include +#include #include #include #include #include #include +#include + namespace AzToolsFramework { namespace Prefab @@ -49,6 +52,7 @@ namespace AzToolsFramework , public AssetBrowser::AssetBrowserSourceDropBus::Handler , public PrefabInstanceContainerNotificationBus::Handler , public PrefabIntegrationInterface + , public QObject { public: AZ_CLASS_ALLOCATOR(PrefabIntegrationManager, AZ::SystemAllocator, 0); @@ -72,6 +76,8 @@ namespace AzToolsFramework // PrefabIntegrationInterface... AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) override; + int ExecuteClosePrefabDialog(TemplateId templateId) override; + void ExecuteSavePrefabsDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override; private: // Manages the Edit Mode UI for prefabs @@ -124,12 +130,19 @@ namespace AzToolsFramework static AZ::u32 GetSliceFlags(const AZ::Edit::ElementData* editData, const AZ::Edit::ClassData* classData); + AZStd::shared_ptr ConstructClosePrefabDialog(TemplateId templateId); + AzQtComponents::Card* ConstructUnsavedPrefabsCard(TemplateId templateId); + AZStd::unique_ptr ConstructSavePrefabsDialog(TemplateId templateId, bool useSaveAllPrefabsPreference); + void SavePrefabsInDialog(QDialog* unsavedPrefabsDialog); + + static const AZStd::string s_prefabFileExtension; static EditorEntityUiInterface* s_editorEntityUiInterface; static PrefabPublicInterface* s_prefabPublicInterface; static PrefabEditInterface* s_prefabEditInterface; static PrefabLoaderInterface* s_prefabLoaderInterface; + static PrefabSystemComponentInterface* s_prefabSystemComponentInterface; }; } }