Merge pull request #3440 from aws-lumberyard-dev/Prefab/SaveAllPrefabs

Provide options to save prefabs when saving levels
monroegm-disable-blank-issue-2
AMZN-koppersr 4 years ago committed by GitHub
commit f9ce966103
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -33,6 +33,7 @@ AZ_POP_DISABLE_WARNING
#include <QScopedValueRollback>
#include <QClipboard>
#include <QMenuBar>
#include <QDialogButtonBox>
// Aws Native SDK
#include <aws/sts/STSClient.h>
@ -717,7 +718,23 @@ void CCryEditApp::OnFileSave()
const QScopedValueRollback<bool> rollback(m_savingLevel, true);
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
if (!usePrefabSystemForLevels)
{
GetIEditor()->GetDocument()->DoFileSave();
}
else
{
auto* prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
auto* prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
AZ_Assert(prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface is not found.");
AZ_Assert(prefabIntegrationInterface != nullptr, "PrefabIntegrationInterface is not found.");
AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId = prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId();
prefabIntegrationInterface->ExecuteSavePrefabDialog(rootPrefabTemplateId, true);
}
}
@ -3127,9 +3144,16 @@ bool CCryEditApp::CreateLevel(bool& wasCreateLevelOperationCancelled)
{
bool bIsDocModified = GetIEditor()->GetDocument()->IsModified();
if (GetIEditor()->GetDocument()->IsDocumentReady() && bIsDocModified)
{
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
if (!usePrefabSystemForLevels)
{
QString str = QObject::tr("Level %1 has been changed. Save Level?").arg(GetIEditor()->GetGameEngine()->GetLevelName());
int result = QMessageBox::question(AzToolsFramework::GetActiveWindow(), QObject::tr("Save Level"), str, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
int result = QMessageBox::question(
AzToolsFramework::GetActiveWindow(), QObject::tr("Save Level"), str,
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (QMessageBox::Yes == result)
{
if (!GetIEditor()->GetDocument()->DoFileSave())
@ -3153,6 +3177,39 @@ bool CCryEditApp::CreateLevel(bool& wasCreateLevelOperationCancelled)
return false;
}
}
else
{
auto* prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
auto* prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
AZ_Assert(prefabEditorEntityOwnershipInterface != nullptr, "PrefabEditorEntityOwnershipInterface is not found.");
AZ_Assert(prefabIntegrationInterface != nullptr, "PrefabIntegrationInterface is not found.");
if (prefabEditorEntityOwnershipInterface == nullptr || prefabIntegrationInterface == nullptr)
{
return false;
}
AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId = prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId();
int prefabSaveSelection = prefabIntegrationInterface->ExecuteClosePrefabDialog(rootPrefabTemplateId);
// In order to get the accept and reject codes of QDialog and QDialogButtonBox aligned, we do (1-prefabSaveSelection) here.
// For example, QDialog::Rejected(0) is emitted when dialog is closed. But the int value corresponds to
// QDialogButtonBox::AcceptRole(0).
switch (1 - prefabSaveSelection)
{
case QDialogButtonBox::AcceptRole:
bIsDocModified = false;
break;
case QDialogButtonBox::RejectRole:
wasCreateLevelOperationCancelled = true;
return false;
case QDialogButtonBox::InvalidRole:
// Set Modified flag to false to prevent show Save unchanged dialog again
GetIEditor()->GetDocument()->SetModifiedFlag(false);
break;
}
}
}
const char* temporaryLevelName = GetIEditor()->GetDocument()->GetTemporaryLevelName();

@ -13,6 +13,7 @@
// Qt
#include <QDateTime>
#include <QDialogButtonBox>
// AzCore
#include <AzCore/Component/TransformBus.h>
@ -30,7 +31,6 @@
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <AzToolsFramework/UI/Layer/NameConflictWarning.hxx>
#include <AzToolsFramework/API/EditorLevelNotificationBus.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
// Editor
#include "Settings.h"
@ -132,6 +132,19 @@ CCryEditDoc::CCryEditDoc()
RegisterConsoleVariables();
MainWindow::instance()->GetActionManager()->RegisterActionHandler(ID_FILE_SAVE_AS, this, &CCryEditDoc::OnFileSaveAs);
bool isPrefabSystemEnabled = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(isPrefabSystemEnabled, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
if (isPrefabSystemEnabled)
{
m_prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
AZ_Assert(m_prefabSystemComponentInterface, "PrefabSystemComponentInterface is not found.");
m_prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
AZ_Assert(m_prefabEditorEntityOwnershipInterface, "PrefabEditorEntityOwnershipInterface is not found.");
m_prefabLoaderInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
AZ_Assert(m_prefabLoaderInterface, "PrefabLoaderInterface is not found.");
m_prefabIntegrationInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabIntegrationInterface>::Get();
AZ_Assert(m_prefabIntegrationInterface, "PrefabIntegrationInterface is not found.");
}
}
CCryEditDoc::~CCryEditDoc()
@ -664,7 +677,18 @@ bool CCryEditDoc::SaveModified()
return true;
}
auto button = QMessageBox::question(AzToolsFramework::GetActiveWindow(), QString(), tr("Save changes to %1?").arg(GetTitle()),
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
if (!usePrefabSystemForLevels)
{
QMessageBox saveModifiedMessageBox(AzToolsFramework::GetActiveWindow());
saveModifiedMessageBox.setText(QString("Save changes to %1?").arg(GetTitle()));
saveModifiedMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
saveModifiedMessageBox.setIcon(QMessageBox::Icon::Question);
auto button = QMessageBox::question(
AzToolsFramework::GetActiveWindow(), QString(), tr("Save changes to %1?").arg(GetTitle()),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
switch (button)
{
@ -677,6 +701,32 @@ bool CCryEditDoc::SaveModified()
return true;
}
Q_UNREACHABLE();
}
else
{
AzToolsFramework::Prefab::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.
// For example, QDialog::Rejected(0) is emitted when dialog is closed. But the int value corresponds to
// QDialogButtonBox::AcceptRole(0).
switch (1 - prefabSaveSelection)
{
case QDialogButtonBox::AcceptRole:
return true;
case QDialogButtonBox::RejectRole:
return false;
case QDialogButtonBox::InvalidRole:
SetModifiedFlag(false);
return true;
}
Q_UNREACHABLE();
}
}
void CCryEditDoc::OnFileSaveAs()
@ -690,6 +740,15 @@ void CCryEditDoc::OnFileSaveAs()
if (OnSaveDocument(levelFileDialog.GetFileName()))
{
CCryEditApp::instance()->AddToRecentFileList(levelFileDialog.GetFileName());
bool usePrefabSystemForLevels = false;
AzFramework::ApplicationRequests::Bus::BroadcastResult(
usePrefabSystemForLevels, &AzFramework::ApplicationRequests::IsPrefabSystemForLevelsEnabled);
if (usePrefabSystemForLevels)
{
AzToolsFramework::Prefab::TemplateId rootPrefabTemplateId =
m_prefabEditorEntityOwnershipInterface->GetRootPrefabTemplateId();
SetModifiedFlag(m_prefabSystemComponentInterface->AreDirtyTemplatesPresent(rootPrefabTemplateId));
}
}
}
}
@ -1238,8 +1297,7 @@ bool CCryEditDoc::SaveLevel(const QString& filename)
}
else
{
auto prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
if (prefabEditorEntityOwnershipInterface)
if (m_prefabEditorEntityOwnershipInterface)
{
AZ::IO::FileIOBase* fileIO = AZ::IO::FileIOBase::GetInstance();
AZ_Assert(fileIO, "No File IO implementation available");
@ -1250,7 +1308,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();
}
}

@ -13,8 +13,13 @@
#if !defined(Q_MOC_RUN)
#include "DocMultiArchive.h"
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/Entity/SliceEditorEntityOwnershipServiceBus.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzCore/Component/Component.h>
#include <AzQtComponents/Components/Widgets/Card.h>
#include <TimeValue.h>
#include <IEditor.h>
#endif
@ -207,6 +212,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

@ -42,6 +42,10 @@ void CEditorPreferencesPage_General::Reflect(AZ::SerializeContext& serialize)
->Field("EnableSceneInspector", &GeneralSettings::m_enableSceneInspector)
->Field("RestoreViewportCamera", &GeneralSettings::m_restoreViewportCamera);
serialize.Class<LevelSaveSettings>()
->Version(1)
->Field("SaveAllPrefabsPreference", &LevelSaveSettings::m_saveAllPrefabsPreference);
serialize.Class<Messaging>()
->Version(2)
->Field("ShowDashboard", &Messaging::m_showDashboard)
@ -64,6 +68,7 @@ void CEditorPreferencesPage_General::Reflect(AZ::SerializeContext& serialize)
serialize.Class<CEditorPreferencesPage_General>()
->Version(1)
->Field("General Settings", &CEditorPreferencesPage_General::m_generalSettings)
->Field("Level Save Settings", &CEditorPreferencesPage_General::m_levelSaveSettings)
->Field("Messaging", &CEditorPreferencesPage_General::m_messaging)
->Field("Undo", &CEditorPreferencesPage_General::m_undo)
->Field("Deep Selection", &CEditorPreferencesPage_General::m_deepSelection)
@ -92,6 +97,14 @@ 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<LevelSaveSettings>("Level Save Settings", "")
->DataElement(
AZ::Edit::UIHandlers::ComboBox, &LevelSaveSettings::m_saveAllPrefabsPreference, "Save All 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>("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")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &Messaging::m_showCircularDependencyError, "Show Error: Circular dependency", "Show an error message when adding a slice instance to the target slice would create a cyclic asset dependency. All other valid overrides will be saved even if this is turned off.");
@ -115,6 +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_levelSaveSettings, "Level Save Settings", "File>Save")
->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")
@ -161,6 +175,9 @@ void CEditorPreferencesPage_General::OnApply()
MainWindow::instance()->AdjustToolBarIconSize(m_generalSettings.m_toolbarIconSize);
}
//prefabs
gSettings.levelSaveSettings.saveAllPrefabsPreference = m_levelSaveSettings.m_saveAllPrefabsPreference;
//undo
gSettings.undoLevels = m_undo.m_undoLevels;
@ -190,6 +207,9 @@ void CEditorPreferencesPage_General::InitializeSettings()
m_generalSettings.m_toolbarIconSize = static_cast<AzQtComponents::ToolBar::ToolBarIconSize>(gSettings.gui.nToolbarIconSize);
//prefabs
m_levelSaveSettings.m_saveAllPrefabsPreference = gSettings.levelSaveSettings.saveAllPrefabsPreference;
//Messaging
m_messaging.m_showDashboard = gSettings.bShowDashboardAtStartup;
m_messaging.m_showCircularDependencyError = gSettings.m_showCircularDependencyError;

@ -14,6 +14,7 @@
#include <AzCore/Math/Vector3.h>
#include <AzQtComponents/Components/Widgets/ToolBar.h>
#include <AzToolsFramework/Editor/EditorSettingsAPIBus.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <QIcon>
#include "Settings.h"
@ -57,6 +58,12 @@ private:
bool m_enableSceneInspector;
};
struct LevelSaveSettings
{
AZ_TYPE_INFO(LevelSaveSettings, "{E297DAE3-3985-4BC2-8B43-45F3B1522F6B}");
AzToolsFramework::Prefab::SaveAllPrefabsPreference m_saveAllPrefabsPreference;
};
struct Messaging
{
AZ_TYPE_INFO(Messaging, "{A6AD87CB-E905-409B-A2BF-C43CDCE63B0C}")
@ -89,6 +96,7 @@ private:
};
GeneralSettings m_generalSettings;
LevelSaveSettings m_levelSaveSettings;
Messaging m_messaging;
Undo m_undo;
DeepSelection m_deepSelection;

@ -241,6 +241,7 @@ SEditorSettings::SEditorSettings()
g_TemporaryLevelName = nullptr;
sliceSettings.dynamicByDefault = false;
levelSaveSettings.saveAllPrefabsPreference = AzToolsFramework::Prefab::SaveAllPrefabsPreference::AskEveryTime;
}
void SEditorSettings::Connect()
@ -643,12 +644,20 @@ void SEditorSettings::Save()
AzFramework::ApplicationRequests::Bus::Broadcast(
&AzFramework::ApplicationRequests::SetPrefabSystemEnabled, prefabSystem);
AzToolsFramework::Prefab::PrefabLoaderInterface* prefabLoaderInterface =
AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
prefabLoaderInterface->SetSaveAllPrefabsPreference(levelSaveSettings.saveAllPrefabsPreference);
SaveSettingsRegistryFile();
}
//////////////////////////////////////////////////////////////////////////
void SEditorSettings::Load()
{
AzToolsFramework::Prefab::PrefabLoaderInterface* prefabLoaderInterface =
AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
levelSaveSettings.saveAllPrefabsPreference = prefabLoaderInterface->GetSaveAllPrefabsPreference();
// Load from Settings Registry
AzFramework::ApplicationRequests::Bus::BroadcastResult(
prefabSystem, &AzFramework::ApplicationRequests::IsPrefabSystemEnabled);
@ -1110,11 +1119,17 @@ void SEditorSettings::SaveSettingsRegistryFile()
AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings;
dumperSettings.m_prettifyOutput = true;
dumperSettings.m_jsonPointerPrefix = "/Amazon/Preferences";
dumperSettings.m_includeFilter = [](AZStd::string_view path)
{
AZStd::string_view amazonPrefixPath("/Amazon/Preferences");
AZStd::string_view o3dePrefixPath("/O3DE/Preferences");
return amazonPrefixPath.starts_with(path.substr(0, amazonPrefixPath.size())) ||
o3dePrefixPath.starts_with(path.substr(0, o3dePrefixPath.size()));
};
AZStd::string stringBuffer;
AZ::IO::ByteContainerStream stringStream(&stringBuffer);
if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(*registry, "/Amazon/Preferences", stringStream, dumperSettings))
if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream(*registry, "", stringStream, dumperSettings))
{
AZ_Warning("SEditorSettings", false, R"(Unable to save changes to the Editor Preferences registry file at "%s"\n)",
editorPreferencesFilePath.c_str());

@ -18,6 +18,7 @@
#include <QSettings>
#include <AzToolsFramework/Editor/EditorSettingsAPIBus.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <AzCore/JSON/document.h>
#include <AzQtComponents/Components/Widgets/ToolBar.h>
@ -214,6 +215,11 @@ struct SSliceSettings
bool dynamicByDefault;
};
struct SLevelSaveSettings
{
AzToolsFramework::Prefab::SaveAllPrefabsPreference saveAllPrefabsPreference;
};
//////////////////////////////////////////////////////////////////////////
struct SAssetBrowserSettings
{
@ -448,6 +454,8 @@ AZ_POP_DISABLE_DLL_EXPORT_BASECLASS_WARNING
SSliceSettings sliceSettings;
SLevelSaveSettings levelSaveSettings;
bool prefabSystem = true; ///< Toggle to enable/disable the Prefab system for level entities.
private:

@ -244,3 +244,41 @@ QTableWidget#recentLevelTable::item {
max-height: 16px;
qproperty-iconSize: 16px 16px;
}
#ClosePrefabDialog, #SavePrefabDialog
{
min-width : 640px;
}
#SaveDependentPrefabsCard
{
margin: 0px 15px 10px 15px;
}
#PrefabSavedMessageFrame{
border: 1px solid green;
margin: 10px 15px 10px 15px;
border-radius: 2px;
padding: 5px 2px 5px 2px;
}
#ClosePrefabDialog #PrefabSaveWarningFrame
{
border: 1px solid orange;
margin: 10px 15px 10px 15px;
border-radius: 2px;
padding: 5px 2px 5px 2px;
color : white;
}
#SavePrefabDialog #FooterSeparatorLine
{
color: gray;
}
#SavePrefabDialog #PrefabSavePreferenceHint
{
font: italic;
color: #999999;
}

@ -342,7 +342,7 @@ namespace AzQtComponents
config.toolTipPaddingInPixels = 5;
config.headerIconSizeInPixels = CardHeader::defaultIconSize();
config.rootLayoutSpacing = 0;
config.warningIcon = QStringLiteral(":/Cards/img/UI20/Cards/warning.svg");
config.warningIcon = QStringLiteral(":/Notifications/warning.svg");
config.warningIconSize = {24, 24};
config.disabledIconAlpha = 0.25;

@ -424,7 +424,6 @@
<file>img/UI20/Cards/menu_ico.png</file>
<file>img/UI20/Cards/error_icon.png</file>
<file>img/UI20/Cards/warning.png</file>
<file>img/UI20/Cards/warning.svg</file>
<file>img/UI20/Cards/search.png</file>
<file>img/UI20/Cards/close.png</file>
<file>img/UI20/Cards/error-conclict-state.svg</file>

@ -30,6 +30,7 @@
<file alias="checkmark.svg">Notifications/checkmark.svg</file>
<file alias="download.svg">Notifications/download.svg</file>
<file alias="link.svg">Notifications/link.svg</file>
<file alias="warning.svg">Notifications/warning.svg</file>
</qresource>
<qresource prefix="/Outliner">
<file alias="sort_a_to_z.svg">Outliner/sort_a_to_z.svg</file>

@ -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<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() = 0;

@ -359,6 +359,12 @@ namespace AzToolsFramework
return AZStd::nullopt;
}
Prefab::TemplateId PrefabEditorEntityOwnershipService::GetRootPrefabTemplateId()
{
AZ_Assert(m_rootInstance, "A valid root prefab instance couldn't be found in PrefabEditorEntityOwnershipService.");
return m_rootInstance ? m_rootInstance->GetTemplateId() : Prefab::InvalidTemplateId;
}
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& PrefabEditorEntityOwnershipService::GetPlayInEditorAssetData()
{
return m_playInEditorData.m_assets;

@ -193,6 +193,7 @@ namespace AzToolsFramework
AZ::IO::PathView filePath, Prefab::InstanceOptionalReference instanceToParentUnder) override;
Prefab::InstanceOptionalReference GetRootPrefabInstance() override;
Prefab::TemplateId GetRootPrefabTemplateId() override;
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& GetPlayInEditorAssetData() override;
//////////////////////////////////////////////////////////////////////////

@ -26,6 +26,19 @@ namespace AzToolsFramework
{
namespace Prefab
{
static constexpr const char s_saveAllPrefabsKey[] = "/O3DE/Preferences/Prefabs/SaveAllPrefabs";
void PrefabLoader::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Enum<SaveAllPrefabsPreference>()
->Value("Ask every time", SaveAllPrefabsPreference::AskEveryTime)
->Value("Save all", SaveAllPrefabsPreference::SaveAll)
->Value("Save none", SaveAllPrefabsPreference::SaveNone);
}
}
void PrefabLoader::RegisterPrefabLoaderInterface()
{
m_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
@ -657,6 +670,24 @@ namespace AzToolsFramework
return finalPath;
}
SaveAllPrefabsPreference PrefabLoader::GetSaveAllPrefabsPreference() const
{
SaveAllPrefabsPreference saveAllPrefabsPreference = SaveAllPrefabsPreference::AskEveryTime;
if (auto* registry = AZ::SettingsRegistry::Get())
{
registry->GetObject(saveAllPrefabsPreference, s_saveAllPrefabsKey);
}
return saveAllPrefabsPreference;
}
void PrefabLoader::SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference)
{
if (auto* registry = AZ::SettingsRegistry::Get())
{
registry->SetObject(s_saveAllPrefabsKey, saveAllPrefabsPreference);
}
}
AZ::IO::Path PrefabLoaderInterface::GeneratePath()
{
return AZStd::string::format("Prefab_%s", AZ::Entity::MakeId().ToString().c_str());

@ -39,6 +39,8 @@ namespace AzToolsFramework
AZ_CLASS_ALLOCATOR(PrefabLoader, AZ::SystemAllocator, 0);
AZ_RTTI(PrefabLoader, "{A302B072-4DC4-4B7E-9188-226F56A3429C8}", PrefabLoaderInterface);
static void Reflect(AZ::ReflectContext* context);
//////////////////////////////////////////////////////////////////////////
// PrefabLoaderInterface interface implementation
@ -108,6 +110,9 @@ namespace AzToolsFramework
//! Returns if the path is a valid path for a prefab
static bool IsValidPrefabPath(AZ::IO::PathView path);
SaveAllPrefabsPreference GetSaveAllPrefabsPreference() const override;
void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) override;
private:
/**
* Copies the template dom provided and manipulates it into the proper format to be saved to disk.

@ -17,6 +17,13 @@ namespace AzToolsFramework
{
namespace Prefab
{
enum class SaveAllPrefabsPreference
{
AskEveryTime,
SaveAll,
SaveNone
};
/*!
* PrefabLoaderInterface
* Interface for saving/loading Prefab files.
@ -84,6 +91,9 @@ namespace AzToolsFramework
//! The path will always use the '/' separator.
virtual AZ::IO::Path GenerateRelativePath(AZ::IO::PathView path) = 0;
virtual SaveAllPrefabsPreference GetSaveAllPrefabsPreference() const = 0;
virtual void SetSaveAllPrefabsPreference(SaveAllPrefabsPreference saveAllPrefabsPreference) = 0;
protected:
// Generates a new path
@ -93,3 +103,8 @@ namespace AzToolsFramework
} // namespace Prefab
} // namespace AzToolsFramework
namespace AZ
{
AZ_TYPE_INFO_SPECIALIZE(AzToolsFramework::Prefab::SaveAllPrefabsPreference, "{7E61EA82-4DE4-4A3F-945F-C8FEDC1114B5}");
}

@ -57,6 +57,7 @@ namespace AzToolsFramework
AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor::Reflect(context);
AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover::Reflect(context);
PrefabPublicRequestHandler::Reflect(context);
PrefabLoader::Reflect(context);
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
@ -369,9 +370,11 @@ namespace AzToolsFramework
PrefabDom& PrefabSystemComponent::FindTemplateDom(TemplateId templateId)
{
AZStd::optional<AZStd::reference_wrapper<Template>> findTemplateResult = FindTemplate(templateId);
AZ_Assert(findTemplateResult.has_value(),
AZ_Assert(
findTemplateResult.has_value(),
"PrefabSystemComponent::FindTemplateDom - Unable to retrieve Prefab template with id: '%llu'. "
"Template could not be found", templateId);
"Template could not be found",
templateId);
AZ_Assert(findTemplateResult->get().IsValid(),
"PrefabSystemComponent::FindTemplateDom - Unable to retrieve Prefab template with id: '%llu'. "
@ -751,6 +754,89 @@ namespace AzToolsFramework
}
}
bool PrefabSystemComponent::AreDirtyTemplatesPresent(TemplateId rootTemplateId)
{
TemplateReference prefabTemplate = FindTemplate(rootTemplateId);
if (!prefabTemplate.has_value())
{
AZ_Assert(false, "Template with id %llu is not found", rootTemplateId);
return false;
}
if (IsTemplateDirty(rootTemplateId))
{
return true;
}
const Template::Links& linkIds = prefabTemplate->get().GetLinks();
for (LinkId linkId : linkIds)
{
auto linkIterator = m_linkIdMap.find(linkId);
if (linkIterator != m_linkIdMap.end())
{
return AreDirtyTemplatesPresent(linkIterator->second.GetSourceTemplateId());
}
}
return false;
}
void PrefabSystemComponent::SaveAllDirtyTemplates(TemplateId rootTemplateId)
{
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = GetDirtyTemplatePaths(rootTemplateId);
for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths)
{
auto dirtyTemplateIterator = m_templateFilePathToIdMap.find(dirtyTemplatePath);
if (dirtyTemplateIterator == m_templateFilePathToIdMap.end())
{
AZ_Assert(false, "Template id for template with path '%s' is not found.", dirtyTemplatePath);
}
else
{
m_prefabLoader.SaveTemplate(dirtyTemplateIterator->second);
}
}
}
AZStd::set<AZ::IO::PathView> PrefabSystemComponent::GetDirtyTemplatePaths(TemplateId rootTemplateId)
{
AZStd::vector<AZ::IO::PathView> dirtyTemplatePathVector;
GetDirtyTemplatePathsHelper(rootTemplateId, dirtyTemplatePathVector);
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths;
dirtyTemplatePaths.insert(dirtyTemplatePathVector.begin(), dirtyTemplatePathVector.end());
return AZStd::move(dirtyTemplatePaths);
}
void PrefabSystemComponent::GetDirtyTemplatePathsHelper(
TemplateId rootTemplateId, AZStd::vector<AZ::IO::PathView>& dirtyTemplatePaths)
{
TemplateReference prefabTemplate = FindTemplate(rootTemplateId);
if (!prefabTemplate.has_value())
{
AZ_Assert(false, "Template with id %llu is not found", rootTemplateId);
return;
}
if (IsTemplateDirty(rootTemplateId))
{
dirtyTemplatePaths.emplace_back(prefabTemplate->get().GetFilePath());
}
const Template::Links& linkIds = prefabTemplate->get().GetLinks();
for (LinkId linkId : linkIds)
{
auto linkIterator = m_linkIdMap.find(linkId);
if (linkIterator != m_linkIdMap.end())
{
GetDirtyTemplatePathsHelper(linkIterator->second.GetSourceTemplateId(), dirtyTemplatePaths);
}
}
}
bool PrefabSystemComponent::ConnectTemplates(
Link& link,
TemplateId sourceTemplateId,

@ -183,6 +183,12 @@ namespace AzToolsFramework
*/
void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) override;
bool AreDirtyTemplatesPresent(TemplateId rootTemplateId) override;
void SaveAllDirtyTemplates(TemplateId rootTemplateId) override;
AZStd::set<AZ::IO::PathView> GetDirtyTemplatePaths(TemplateId rootTemplateId) override;
//////////////////////////////////////////////////////////////////////////
/**
@ -335,6 +341,9 @@ namespace AzToolsFramework
*/
bool RemoveLinkFromTargetTemplate(const LinkId& linkId, const Link& link);
// Helper function for GetDirtyTemplatePaths(). It uses vector to speed up iteration times.
void GetDirtyTemplatePathsHelper(TemplateId rootTemplateId, AZStd::vector<AZ::IO::PathView>& dirtyTemplatePaths);
// A container for mapping Templates to the Links they may propagate changes to.
AZStd::unordered_map<TemplateId, AZStd::unordered_set<LinkId>> m_templateToLinkIdsMap;

@ -9,11 +9,12 @@
#pragma once
#include <AzCore/Interface/Interface.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Link/Link.h>
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
#include <AzToolsFramework/Prefab/Template/Template.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AzToolsFramework
{
@ -50,6 +51,19 @@ namespace AzToolsFramework
virtual bool IsTemplateDirty(const TemplateId& templateId) = 0;
virtual void SetTemplateDirtyFlag(const TemplateId& templateId, bool dirty) = 0;
//! Recursive function to check if the template is dirty or if any dirty templates are presents in the links of the template.
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
virtual bool AreDirtyTemplatesPresent(TemplateId rootTemplateId) = 0;
//! Recursive function to save if the template is dirty and save all the dirty templates in the links of the template.
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
virtual void SaveAllDirtyTemplates(TemplateId rootTemplateId) = 0;
//! Recursive function that fetches the set of dirty templates given a starting template to check for outgoing links.
//! @param rootTemplateId The id of the template provided as the beginning template to check the outgoing links.
//! @return The set of dirty template paths populated.
virtual AZStd::set<AZ::IO::PathView> GetDirtyTemplatePaths(TemplateId rootTemplateId) = 0;
virtual PrefabDom& FindTemplateDom(TemplateId templateId) = 0;
virtual void UpdatePrefabTemplate(TemplateId templateId, const PrefabDom& updatedDom) = 0;
virtual void PropagateTemplateChanges(TemplateId templateId, InstanceOptionalReference instanceToExclude = AZStd::nullopt) = 0;

@ -11,6 +11,7 @@
#include <AzCore/Interface/Interface.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
namespace AzToolsFramework
{
@ -28,6 +29,15 @@ namespace AzToolsFramework
* @return The id of the newly created entity.
*/
virtual AZ::EntityId CreateNewEntityAtPosition(const AZ::Vector3& position, AZ::EntityId parentId) = 0;
//! Constructs and executes the close dialog on a prefab template corresponding to templateId.
//! @param templateId The id of the template the user chose to close.
virtual int ExecuteClosePrefabDialog(TemplateId templateId) = 0;
//! Constructs and executes the save dialog on a prefab template corresponding to templateId.
//! @param templateId The id of the template the user chose to save.
//! @param useSaveAllPrefabsPreference A flag indicating whether SaveAllPrefabsPreference should be used for saving templates.
virtual void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference = false) = 0;
};
} // namespace Prefab

@ -12,6 +12,7 @@
#include <AzCore/IO/SystemFile.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Asset/AssetSystemBus.h>
@ -27,12 +28,28 @@
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/UICore/WidgetHelpers.h>
#include <AzQtComponents/Components/Widgets/CheckBox.h>
#include <AzQtComponents/Components/FlowLayout.h>
#include <AzQtComponents/Components/StyleManager.h>
#include <AzQtComponents/Components/Widgets/CardHeader.h>
#include <QApplication>
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QMainWindow>
#include <QMenu>
#include <QMessageBox>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QWidget>
namespace AzToolsFramework
{
@ -43,9 +60,20 @@ 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";
static const char* const ClosePrefabDialog = "ClosePrefabDialog";
static const char* const FooterSeparatorLine = "FooterSeparatorLine";
static const char* const PrefabSavedMessageFrame = "PrefabSavedMessageFrame";
static const char* const PrefabSavePreferenceHint = "PrefabSavePreferenceHint";
static const char* const PrefabSaveWarningFrame = "PrefabSaveWarningFrame";
static const char* const SaveDependentPrefabsCard = "SaveDependentPrefabsCard";
static const char* const SavePrefabDialog = "SavePrefabDialog";
static const char* const UnsavedPrefabFileName = "UnsavedPrefabFileName";
void PrefabUserSettings::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
@ -88,6 +116,13 @@ namespace AzToolsFramework
return;
}
s_prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::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<PrefabIntegrationInterface>::Register(this);
@ -1050,5 +1085,239 @@ namespace AzToolsFramework
return AZ::EntityId();
}
}
int PrefabIntegrationManager::ExecuteClosePrefabDialog(TemplateId templateId)
{
if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId))
{
auto prefabSaveSelectionDialog = ConstructClosePrefabDialog(templateId);
int prefabSaveSelection = prefabSaveSelectionDialog->exec();
if (prefabSaveSelection == QDialog::Accepted)
{
SavePrefabsInDialog(prefabSaveSelectionDialog.get());
}
return prefabSaveSelection;
}
return QDialogButtonBox::DestructiveRole;
}
void PrefabIntegrationManager::ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference)
{
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("Prefab", false, "Template '%s' could not be saved successfully.", prefabTemplatePath.c_str());
return;
}
}
if (s_prefabSystemComponentInterface->AreDirtyTemplatesPresent(templateId))
{
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<QDialog> savePrefabDialog = ConstructSavePrefabDialog(templateId, useSaveAllPrefabsPreference);
if (savePrefabDialog)
{
int prefabSaveSelection = savePrefabDialog->exec();
if (prefabSaveSelection == QDialog::Accepted)
{
SavePrefabsInDialog(savePrefabDialog.get());
}
}
}
}
void PrefabIntegrationManager::SavePrefabsInDialog(QDialog* unsavedPrefabsDialog)
{
QList<QLabel*> unsavedPrefabFileLabels = unsavedPrefabsDialog->findChildren<QLabel*>(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_Error("Prefab", isTemplateSavedSuccessfully, "Prefab '%s' could not be saved successfully.", unsavedPrefabFileName.c_str());
}
}
}
AZStd::unique_ptr<QDialog> PrefabIntegrationManager::ConstructSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference)
{
AZStd::unique_ptr<QDialog> savePrefabDialog = AZStd::make_unique<QDialog>(AzToolsFramework::GetActiveWindow());
savePrefabDialog->setWindowTitle("Unsaved files detected");
// Main Content section begins.
savePrefabDialog->setObjectName(SavePrefabDialog);
QBoxLayout* contentLayout = new QVBoxLayout(savePrefabDialog.get());
QFrame* prefabSavedMessageFrame = new QFrame(savePrefabDialog.get());
QHBoxLayout* prefabSavedMessageLayout = new QHBoxLayout(savePrefabDialog.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(savePrefabDialog.get());
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 '<b>%1</b>' has been saved. Do you want to save the below dependent prefabs too?").arg(prefabTemplatePath.c_str()),
savePrefabDialog.get());
prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyIconContainer);
prefabSavedMessageLayout->addWidget(prefabSavedSuccessfullyLabel);
prefabSavedMessageFrame->setLayout(prefabSavedMessageLayout);
contentLayout->addWidget(prefabSavedMessageFrame);
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsContainer = ConstructUnsavedPrefabsCard(templateId);
contentLayout->addWidget(unsavedPrefabsContainer.release());
contentLayout->addStretch();
// Footer section begins.
QHBoxLayout* footerLayout = new QHBoxLayout(savePrefabDialog.get());
if (useSaveAllPrefabsPreference)
{
QFrame* footerSeparatorLine = new QFrame(savePrefabDialog.get());
footerSeparatorLine->setObjectName(FooterSeparatorLine);
footerSeparatorLine->setFrameShape(QFrame::HLine);
contentLayout->addWidget(footerSeparatorLine);
QLabel* prefabSavePreferenceHint = new QLabel(
"<u>You can prevent this window from showing in the future by updating your global save preferences.</u>",
savePrefabDialog.get());
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, savePrefabDialog.get());
footerLayout->addWidget(prefabSaveConfirmationButtons);
contentLayout->addLayout(footerLayout);
connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, savePrefabDialog.get(), &QDialog::accept);
connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, savePrefabDialog.get(), &QDialog::reject);
AzQtComponents::StyleManager::setStyleSheet(savePrefabDialog->parentWidget(), QStringLiteral("style:Editor.qss"));
savePrefabDialog->setLayout(contentLayout);
return AZStd::move(savePrefabDialog);
}
AZStd::shared_ptr<QDialog> PrefabIntegrationManager::ConstructClosePrefabDialog(TemplateId templateId)
{
AZStd::shared_ptr<QDialog> closePrefabDialog = AZStd::make_shared<QDialog>(AzToolsFramework::GetActiveWindow());
closePrefabDialog->setWindowTitle("Unsaved files detected");
AZStd::weak_ptr<QDialog> closePrefabDialogWeakPtr(closePrefabDialog);
closePrefabDialog->setObjectName(ClosePrefabDialog);
// Main Content section begins.
QVBoxLayout* contentLayout = new QVBoxLayout(closePrefabDialog.get());
QFrame* prefabSaveWarningFrame = new QFrame(closePrefabDialog.get());
QHBoxLayout* levelEntitiesSaveQuestionLayout = new QHBoxLayout(closePrefabDialog.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(closePrefabDialog.get());
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?", closePrefabDialog.get());
levelEntitiesSaveQuestionLayout->addWidget(prefabSaveQuestionLabel);
contentLayout->addWidget(prefabSaveWarningFrame);
auto templateToSave = s_prefabSystemComponentInterface->FindTemplate(templateId);
AZ::IO::Path templateToSaveFilePath = templateToSave->get().GetFilePath();
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsCard = ConstructUnsavedPrefabsCard(templateId);
contentLayout->addWidget(unsavedPrefabsCard.release());
contentLayout->addStretch();
QHBoxLayout* footerLayout = new QHBoxLayout(closePrefabDialog.get());
QDialogButtonBox* prefabSaveConfirmationButtons = new QDialogButtonBox(
QDialogButtonBox::Save | QDialogButtonBox::Discard | QDialogButtonBox::Cancel, closePrefabDialog.get());
footerLayout->addWidget(prefabSaveConfirmationButtons);
contentLayout->addLayout(footerLayout);
QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::accepted, closePrefabDialog.get(), &QDialog::accept);
QObject::connect(prefabSaveConfirmationButtons, &QDialogButtonBox::rejected, closePrefabDialog.get(), &QDialog::reject);
QObject::connect(
prefabSaveConfirmationButtons, &QDialogButtonBox::clicked, closePrefabDialog.get(),
[closePrefabDialogWeakPtr, prefabSaveConfirmationButtons](QAbstractButton* button)
{
int prefabSaveSelection = prefabSaveConfirmationButtons->buttonRole(button);
closePrefabDialogWeakPtr.lock()->done(prefabSaveSelection);
});
AzQtComponents::StyleManager::setStyleSheet(closePrefabDialog.get(), QStringLiteral("style:Editor.qss"));
closePrefabDialog->setLayout(contentLayout);
return closePrefabDialog;
}
AZStd::unique_ptr<AzQtComponents::Card> PrefabIntegrationManager::ConstructUnsavedPrefabsCard(TemplateId templateId)
{
FlowLayout* unsavedPrefabsLayout = new FlowLayout(AzToolsFramework::GetActiveWindow());
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = s_prefabSystemComponentInterface->GetDirtyTemplatePaths(templateId);
for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths)
{
QLabel* prefabNameLabel =
new QLabel(QString("<u>%1</u>").arg(dirtyTemplatePath.Filename().Native().data()), AzToolsFramework::GetActiveWindow());
prefabNameLabel->setObjectName(UnsavedPrefabFileName);
prefabNameLabel->setWordWrap(true);
prefabNameLabel->setToolTip(dirtyTemplatePath.Native().data());
prefabNameLabel->setProperty("FilePath", dirtyTemplatePath.Native().data());
unsavedPrefabsLayout->addWidget(prefabNameLabel);
}
AZStd::unique_ptr<AzQtComponents::Card> unsavedPrefabsContainer = AZStd::make_unique<AzQtComponents::Card>(AzToolsFramework::GetActiveWindow());
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.get());
unsavedPrefabsFrame->setLayout(unsavedPrefabsLayout);
QScrollArea* unsavedPrefabsScrollArea = new QScrollArea(unsavedPrefabsContainer.get());
unsavedPrefabsScrollArea->setWidget(unsavedPrefabsFrame);
unsavedPrefabsScrollArea->setWidgetResizable(true);
unsavedPrefabsContainer->setContentWidget(unsavedPrefabsScrollArea);
return AZStd::move(unsavedPrefabsContainer);
}
}
}

@ -15,12 +15,15 @@
#include <AzToolsFramework/AssetBrowser/AssetBrowserSourceDropBus.h>
#include <AzToolsFramework/Editor/EditorContextMenuBus.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabEditManager.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
#include <AzQtComponents/Components/Widgets/Card.h>
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 ExecuteSavePrefabDialog(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<QDialog> ConstructClosePrefabDialog(TemplateId templateId);
AZStd::unique_ptr<AzQtComponents::Card> ConstructUnsavedPrefabsCard(TemplateId templateId);
AZStd::unique_ptr<QDialog> ConstructSavePrefabDialog(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;
};
}
}

Loading…
Cancel
Save