Skip to content

Commit dcbab80

Browse files
authored
Merge pull request o3de#17612 from aws-lumberyard-dev/daimini/Prefabs/ApplyOverride
Prefabs | Apply all overrides to a component one level down
2 parents 6759fd0 + b202329 commit dcbab80

File tree

9 files changed

+440
-35
lines changed

9 files changed

+440
-35
lines changed

Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverrideHandler.cpp

Lines changed: 205 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
*
77
*/
88

9-
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
109
#include <AzToolsFramework/Prefab/Overrides/PrefabOverrideHandler.h>
10+
11+
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
1112
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
1213
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
14+
#include <AzToolsFramework/Prefab/Undo/PrefabUndoApplyOverrides.h>
15+
#include <AzToolsFramework/Prefab/Undo/PrefabUndoComponentPropertyEdit.h>
1316
#include <AzToolsFramework/Prefab/Undo/PrefabUndoRevertOverrides.h>
1417

1518
namespace AzToolsFramework
@@ -37,39 +40,6 @@
3740
return false;
3841
}
3942

40-
bool PrefabOverrideHandler::RevertOverrides(AZ::Dom::Path path, LinkId linkId) const
41-
{
42-
LinkReference link = m_prefabSystemComponentInterface->FindLink(linkId);
43-
if (link.has_value())
44-
{
45-
auto subTree = link->get().RemoveOverrides(path);
46-
if (subTree.IsEmpty())
47-
{
48-
AZ_Warning("Prefab", false, "PrefabOverrideHandler::RevertOverrides is called on a path that has no overrides.");
49-
return false;
50-
}
51-
52-
ScopedUndoBatch undoBatch("Revert Prefab Overrides");
53-
PrefabUndoRevertOverrides* state = new Prefab::PrefabUndoRevertOverrides("Capture Override SubTree");
54-
state->Capture(path, AZStd::move(subTree), linkId);
55-
state->SetParent(undoBatch.GetUndoBatch());
56-
57-
link->get().UpdateTarget();
58-
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(link->get().GetTargetTemplateId(), true);
59-
m_prefabSystemComponentInterface->PropagateTemplateChanges(link->get().GetTargetTemplateId());
60-
61-
// Queue a refresh of the property display in case the overrides contain only default values.
62-
// Otherwise, when overrides don't trigger changes in the template dom, the entity won't be
63-
// recreated on propagation and the inspector won't be updated to reflect override icon changes
64-
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
65-
&AzToolsFramework::ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay,
66-
AzToolsFramework::Refresh_AttributesAndValues);
67-
68-
return true;
69-
}
70-
return false;
71-
}
72-
7343
AZStd::optional<PatchType> PrefabOverrideHandler::GetPatchType(AZ::Dom::Path path, LinkId linkId) const
7444
{
7545
AZStd::optional<PatchType> patchType = {};
@@ -107,5 +77,206 @@
10777

10878
return patchType;
10979
}
80+
81+
bool PrefabOverrideHandler::RevertOverrides(AZ::Dom::Path path, LinkId linkId) const
82+
{
83+
LinkReference link = m_prefabSystemComponentInterface->FindLink(linkId);
84+
if (!link.has_value())
85+
{
86+
return false;
87+
}
88+
89+
auto subTree = link->get().RemoveOverrides(path);
90+
if (subTree.IsEmpty())
91+
{
92+
AZ_Warning("Prefab", false, "PrefabOverrideHandler::RevertOverrides is called on a path that has no overrides.");
93+
return false;
94+
}
95+
96+
ScopedUndoBatch undoBatch("Revert Prefab Overrides");
97+
PrefabUndoRevertOverrides* state = new Prefab::PrefabUndoRevertOverrides("Capture Override SubTree");
98+
state->Capture(path, AZStd::move(subTree), linkId);
99+
state->SetParent(undoBatch.GetUndoBatch());
100+
101+
link->get().UpdateTarget();
102+
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(link->get().GetTargetTemplateId(), true);
103+
m_prefabSystemComponentInterface->PropagateTemplateChanges(link->get().GetTargetTemplateId());
104+
105+
// Queue a refresh of the property display in case the overrides contain only default values.
106+
// Otherwise, when overrides don't trigger changes in the template dom, the entity won't be
107+
// recreated on propagation and the inspector won't be updated to reflect override icon changes
108+
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
109+
&AzToolsFramework::ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay,
110+
AzToolsFramework::Refresh_AttributesAndValues);
111+
112+
return true;
113+
}
114+
115+
bool PrefabOverrideHandler::PushOverridesToPrefab(
116+
const AZ::Dom::Path& path,
117+
AZStd::string_view relativePath,
118+
InstanceOptionalReference targetInstance
119+
) const
120+
{
121+
// Retrieve the link between the instance holding the overrides (focused) and the one receiving them (targetInstance).
122+
LinkId linkId = targetInstance->get().GetLinkId();
123+
LinkReference link = m_prefabSystemComponentInterface->FindLink(linkId);
124+
125+
// Start an undo batch.
126+
ScopedUndoBatch undoBatch("Apply Overrides to Prefab");
127+
128+
// Remove Overrides in the link.
129+
PrefabOverridePrefixTree subTree = link->get().RemoveOverrides(path);
130+
if (subTree.IsEmpty())
131+
{
132+
AZ_Warning("Prefab", false, "PrefabOverrideHandler::PushOverrides is called on a path that has no overrides.");
133+
return false;
134+
}
135+
136+
// Visit all nodes to apply patches as edits one by one, with the correct path.
137+
subTree.VisitPath(
138+
AZ::Dom::Path(""),
139+
[&undoBatch, &targetInstance, relativePath](
140+
[[maybe_unused]] const AZ::Dom::Path& path, [[maybe_unused]] Link::PrefabOverrideMetadata& metaData) -> bool
141+
{
142+
const auto& overridePath = metaData.m_patch.FindMember("path")->value;
143+
144+
AZStd::string overridePathStr = overridePath.GetString();
145+
if (overridePathStr.starts_with(relativePath))
146+
{
147+
overridePathStr = overridePathStr.substr(relativePath.length());
148+
}
149+
150+
// Apply individual overrides to target prefab template.
151+
PrefabUndoComponentPropertyEdit* state = aznew PrefabUndoComponentPropertyEdit("Apply Override");
152+
state->SetParent(undoBatch.GetUndoBatch());
153+
state->Capture(targetInstance->get(), AZ::Dom::Path(overridePathStr).ToString(), metaData.m_patch.FindMember("value")->value);
154+
state->Redo();
155+
156+
return true;
157+
},
158+
AZ::Dom::PrefixTreeTraversalFlags::None
159+
);
160+
161+
// Create node for overrides reversal so that they get restored on undo.
162+
PrefabUndoRevertOverrides* state = new Prefab::PrefabUndoRevertOverrides("Capture Override SubTree");
163+
state->Capture(path, AZStd::move(subTree), linkId);
164+
state->SetParent(undoBatch.GetUndoBatch());
165+
166+
// Queue a refresh of the property display.
167+
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
168+
&AzToolsFramework::ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay,
169+
AzToolsFramework::Refresh_AttributesAndValues);
170+
171+
return true;
172+
}
173+
174+
bool PrefabOverrideHandler::PushOverridesToLink(
175+
const AZ::Dom::Path& path,
176+
AZStd::string_view relativePath,
177+
LinkId sourceLinkId,
178+
LinkId targetLinkId
179+
) const
180+
{
181+
// Retrieve link.
182+
LinkReference sourceLink = m_prefabSystemComponentInterface->FindLink(sourceLinkId);
183+
LinkReference targetLink = m_prefabSystemComponentInterface->FindLink(targetLinkId);
184+
185+
// Remove relativePath from pathStr so that the patches from sourceLink can be applied to targetLink.
186+
AZStd::string pathStr = path.ToString();
187+
if (pathStr.starts_with(relativePath))
188+
{
189+
pathStr = pathStr.substr(relativePath.length());
190+
}
191+
192+
// Start an undo batch.
193+
ScopedUndoBatch undoBatch("Apply Overrides to Prefab");
194+
195+
// Remove Overrides in sourceLink.
196+
PrefabOverridePrefixTree subTree = sourceLink->get().RemoveOverrides(path);
197+
if (subTree.IsEmpty())
198+
{
199+
AZ_Warning("Prefab", false, "PrefabOverrideHandler::PushOverrides is called on a path that has no overrides.");
200+
return false;
201+
}
202+
203+
// Create a new overrides PrefabOverridePrefixTree that will be altered and passed to the target link.
204+
PrefabOverridePrefixTree newOverrides;
205+
206+
subTree.VisitPath(
207+
AZ::Dom::Path(""),
208+
[&newOverrides, relativePath](
209+
const AZ::Dom::Path& path, Link::PrefabOverrideMetadata& metaData) -> bool
210+
{
211+
// Patches in subTree were retrieved from the source link, so they will contain a path property
212+
// with the relative path from source link to the property being overridden.
213+
// Given that we now want to move these to the target path, we should alter the path so that it is
214+
// pointing from target link to the property being overridden.
215+
// Since the target link is always a descendant of the source link, we are able to do this
216+
// by simply removing the relative path from source to target link from the beginning of the path.
217+
218+
// Copy the patch so that subTree is untouched and can be used for undo/redo.
219+
PrefabDom newPatch;
220+
newPatch.CopyFrom(metaData.m_patch, newPatch.GetAllocator());
221+
222+
auto& overridePath = newPatch.FindMember("path")->value;
223+
AZStd::string overridePathStr = overridePath.GetString();
224+
if (overridePathStr.starts_with(relativePath))
225+
{
226+
overridePathStr = overridePathStr.substr(relativePath.length());
227+
}
228+
229+
overridePath.SetString(
230+
overridePathStr.c_str(),
231+
static_cast<rapidjson::SizeType>(overridePathStr.length()),
232+
newPatch.GetAllocator()
233+
);
234+
235+
// We copy the paths to newOverrides, which is what will be added to the target link.
236+
newOverrides.SetValue<Link::PrefabOverrideMetadata>(
237+
path,
238+
Link::PrefabOverrideMetadata(
239+
AZStd::move(newPatch),
240+
metaData.m_patchIndex
241+
)
242+
);
243+
244+
return true;
245+
},
246+
AZ::Dom::PrefixTreeTraversalFlags::None
247+
);
248+
249+
// Create node for overrides reversal on source link so that they get restored on undo.
250+
PrefabUndoRevertOverrides* sourceState = new Prefab::PrefabUndoRevertOverrides("Capture Override SubTree");
251+
sourceState->Capture(path, AZStd::move(subTree), sourceLinkId);
252+
sourceState->SetParent(undoBatch.GetUndoBatch());
253+
254+
// Create node for overrides addition on target link so that they get removed on undo.
255+
PrefabUndoApplyOverrides* targetState = new Prefab::PrefabUndoApplyOverrides("Apply Overrides");
256+
targetState->Capture(path, AZStd::move(newOverrides), targetLinkId);
257+
targetState->SetParent(undoBatch.GetUndoBatch());
258+
259+
// Actually apply the overrides.
260+
targetState->Redo();
261+
262+
// Correctly update both links.
263+
sourceLink->get().UpdateTarget();
264+
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(sourceLink->get().GetTargetTemplateId(), true);
265+
m_prefabSystemComponentInterface->PropagateTemplateChanges(sourceLink->get().GetTargetTemplateId());
266+
267+
targetLink->get().UpdateTarget();
268+
m_prefabSystemComponentInterface->SetTemplateDirtyFlag(targetLink->get().GetTargetTemplateId(), true);
269+
m_prefabSystemComponentInterface->PropagateTemplateChanges(targetLink->get().GetTargetTemplateId());
270+
271+
// Queue a refresh of the property display in case the overrides contain only default values.
272+
// Otherwise, when overrides don't trigger changes in the template dom, the entity won't be
273+
// recreated on propagation and the inspector won't be updated to reflect override icon changes
274+
AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
275+
&AzToolsFramework::ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay,
276+
AzToolsFramework::Refresh_AttributesAndValues);
277+
278+
return true;
279+
}
280+
110281
} // namespace Prefab
111282
} // namespace AzToolsFramework

Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Overrides/PrefabOverrideHandler.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#pragma once
1010

1111
#include <AzCore/DOM/DomPath.h>
12+
#include <AzToolsFramework/Prefab/Instance/Instance.h>
1213
#include <AzToolsFramework/Prefab/Overrides/PrefabOverrideTypes.h>
1314
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
1415

@@ -41,6 +42,23 @@ namespace AzToolsFramework
4142
//! @return Whether overrides are reverted successfully.
4243
bool RevertOverrides(AZ::Dom::Path path, LinkId linkId) const;
4344

45+
//! Pushes overrides corresponding to the provided path to the prefab template of the prefab instance.
46+
//! @param path The path at which overrides should be applied.
47+
//! @param relativePath The relative path from the instance currently holding the overrides to the one that will receive them.
48+
//! @param targetInstance The instance whose prefab template the overrides will be pushed to.
49+
//! @return Whether overrides are applied successfully.
50+
bool PushOverridesToPrefab(const AZ::Dom::Path& path, AZStd::string_view relativePath, InstanceOptionalReference targetInstance) const;
51+
52+
//! Pushes overrides corresponding to the provided path from one link to another.
53+
//! For this to work, overrides should target a descendant of both source and target links,
54+
//! and targetLink should be a descendant of sourceLink.
55+
//! @param path The path at which overrides should be applied.
56+
//! @param relativePath The relative path from the instance currently holding the overrides to the one that will receive them.
57+
//! @param sourceLinkId The id for the link currently holding the overrides.
58+
//! @param targetLinkId The id for the link that will be receive the overrides.
59+
//! @return Whether overrides are applied successfully.
60+
bool PushOverridesToLink(const AZ::Dom::Path& path, AZStd::string_view relativePath, LinkId sourceLinkId, LinkId targetLinkId) const;
61+
4462
private:
4563
PrefabSystemComponentInterface* m_prefabSystemComponentInterface = nullptr;
4664
};

0 commit comments

Comments
 (0)