Merge branch 'development' of https://github.com/o3de/o3de into cgalvan/ReadOnlyViewportChanges

This commit is contained in:
Chris Galvan
2021-12-06 14:24:19 -06:00
38 changed files with 848 additions and 140 deletions
@@ -12,6 +12,7 @@ import pytest
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
from Atom.atom_utils.atom_constants import LIGHT_TYPES
logger = logging.getLogger(__name__)
@@ -159,3 +160,17 @@ class TestMaterialEditorBasicTests(object):
enable_prefab_system=False,
)
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
@pytest.mark.test_case_id("C36529666")
class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module
@pytest.mark.test_case_id("C36525660")
class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module
@@ -18,6 +18,13 @@ LIGHT_TYPES = {
'simple_spot': 7,
}
# Qualiity Level settings for Diffuse Global Illumination level component
GLOBAL_ILLUMINATION_QUALITY = {
'Low': 0,
'Medium': 1,
'High': 2,
}
class AtomComponentProperties:
"""
@@ -116,6 +123,21 @@ class AtomComponentProperties:
}
return properties[property]
@staticmethod
def diffuse_global_illumination(property: str = 'name') -> str:
"""
Diffuse Global Illumination level component properties.
Controls global settings for Diffuse Probe Grid components.
- 'Quality Level' from atom_constants.py GLOBAL_ILLUMINATION_QUALITY
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'Diffuse Global Illumination',
'Quality Level': 'Controller|Configuration|Quality Level'
}
return properties[property]
@staticmethod
def diffuse_probe_grid(property: str = 'name') -> str:
"""
@@ -148,7 +170,8 @@ class AtomComponentProperties:
@staticmethod
def display_mapper(property: str = 'name') -> str:
"""
Display Mapper component properties.
Display Mapper level component properties.
- 'Enable LDR color grading LUT' toggles the use of LDR color grading LUT
- 'LDR color Grading LUT' is the Low Definition Range (LDR) color grading for Look-up Textures (LUT) which is
an Asset.id value corresponding to a lighting asset file.
:param property: From the last element of the property tree path. Default 'name' for component name string.
@@ -156,6 +179,7 @@ class AtomComponentProperties:
"""
properties = {
'name': 'Display Mapper',
'Enable LDR color grading LUT': 'Controller|Configuration|Enable LDR color grading LUT',
'LDR color Grading LUT': 'Controller|Configuration|LDR color Grading LUT',
}
return properties[property]
@@ -0,0 +1,109 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
creation_undo = (
"UNDO Level component addition success",
"UNDO Level component addition failed")
creation_redo = (
"REDO Level component addition success",
"REDO Level component addition failed")
diffuse_global_illumination_component = (
"Level has a Diffuse Global Illumination component",
"Level failed to find Diffuse Global Illumination component")
diffuse_global_illumination_quality = (
"Quality Level set",
"Quality Level could not be set")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity():
"""
Summary:
Tests the Diffuse Global Illumination level component can be added to the level entity and is stable.
Test setup:
- Wait for Editor idle loop.
- Open the "Base" level.
Expected Behavior:
The component can be added, used in game mode, and has accurate required components.
Creation and deletion undo/redo should also work.
Test Steps:
1) Add Diffuse Global Illumination level component to the level entity.
2) UNDO the level component addition.
3) REDO the level component addition.
4) Set Quality Level property to Low
5) Enter/Exit game mode.
6) Look for errors and asserts.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorLevelEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
from Atom.atom_utils.atom_constants import AtomComponentProperties, GLOBAL_ILLUMINATION_QUALITY
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Add Diffuse Global Illumination level component to the level entity.
diffuse_global_illumination_component = EditorLevelEntity.add_component(
AtomComponentProperties.diffuse_global_illumination())
Report.critical_result(
Tests.diffuse_global_illumination_component,
EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
# 2. UNDO the level component addition.
# -> UNDO component addition.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.creation_undo,
not EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
# 3. REDO the level component addition.
# -> REDO component addition.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.creation_redo,
EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
# 4. Set Quality Level property to Low
diffuse_global_illumination_component.set_component_property_value(
AtomComponentProperties.diffuse_global_illumination('Quality Level', GLOBAL_ILLUMINATION_QUALITY['Low']))
quality = diffuse_global_illumination_component.get_component_property_value(
AtomComponentProperties.diffuse_global_illumination('Quality Level'))
Report.result(diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low'])
# 5. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Look for errors and asserts.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity)
@@ -0,0 +1,124 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
creation_undo = (
"UNDO level component addition success",
"UNDO level component addition failed")
creation_redo = (
"REDO Level component addition success",
"REDO Level component addition failed")
display_mapper_component = (
"Level has a Display Mapper component",
"Level failed to find Display Mapper component")
ldr_color_grading_lut = (
"LDR color Grading LUT asset set",
"LDR color Grading LUT asset could not be set")
enable_ldr_color_grading_lut = (
"Enable LDR color grading LUT set",
"Enable LDR color grading LUT could not be set")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity():
"""
Summary:
Tests the Display Mapper level component can be added to the level entity and has the expected functionality.
Test setup:
- Wait for Editor idle loop.
- Open the "Base" level.
Expected Behavior:
The component can be added, used in game mode, and has accurate required components.
Creation and deletion undo/redo should also work.
Test Steps:
1) Add Display Mapper level component to the level entity.
2) UNDO the level component addition.
3) REDO the level component addition.
4) Set LDR color Grading LUT asset.
5) Set Enable LDR color grading LUT property True
6) Enter/Exit game mode.
7) Look for errors and asserts.
:return: None
"""
import os
import azlmbr.legacy.general as general
from editor_python_test_tools.asset_utils import Asset
from editor_python_test_tools.editor_entity_utils import EditorLevelEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Add Display Mapper level component to the level entity.
display_mapper_component = EditorLevelEntity.add_component(AtomComponentProperties.display_mapper())
Report.critical_result(
Tests.display_mapper_component,
EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
# 2. UNDO the level component addition.
# -> UNDO component addition.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.creation_undo, not EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
# 3. REDO the level component addition.
# -> REDO component addition.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
# 4. Set LDR color Grading LUT asset.
display_mapper_asset_path = os.path.join("TestData", "test.lightingpreset.azasset")
display_mapper_asset = Asset.find_asset_by_path(display_mapper_asset_path, False)
display_mapper_component.set_component_property_value(
AtomComponentProperties.display_mapper('LDR color Grading LUT'), display_mapper_asset.id)
Report.result(
Tests.ldr_color_grading_lut,
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper('LDR color Grading LUT')) == display_mapper_asset.id)
# 5. Set Enable LDR color grading LUT property True
display_mapper_component.set_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True)
Report.result(
Test.enable_ldr_color_grading_lut,
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True)
# 6. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 7. Look for errors and asserts.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AtomEditorComponentsLevel_DisplayMapper_AddedToEntity)
@@ -7,15 +7,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
class Tests:
camera_creation = (
"Camera Entity successfully created",
"Camera Entity failed to be created")
camera_component_added = (
"Camera component was added to entity",
"Camera component failed to be added to entity")
camera_component_check = (
"Entity has a Camera component",
"Entity failed to find Camera component")
creation_undo = (
"UNDO Entity creation success",
"UNDO Entity creation failed")
@@ -43,6 +34,9 @@ class Tests:
ldr_color_grading_lut = (
"LDR color Grading LUT asset set",
"LDR color Grading LUT asset could not be set")
enable_ldr_color_grading_lut = (
"Enable LDR color grading LUT set",
"Enable LDR color grading LUT could not be set")
entity_deleted = (
"Entity deleted",
"Entity was not deleted")
@@ -72,14 +66,15 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
2) Add Display Mapper component to Display Mapper entity.
3) UNDO the entity creation and component addition.
4) REDO the entity creation and component addition.
5) Enter/Exit game mode.
6) Test IsHidden.
7) Test IsVisible.
8) Set LDR color Grading LUT asset.
9) Delete Display Mapper entity.
10) UNDO deletion.
11) REDO deletion.
12) Look for errors and asserts.
5) Set LDR color Grading LUT asset.
6) Set Enable LDR color grading LUT property True
7) Enter/Exit game mode.
8) Test IsHidden.
9) Test IsVisible.
10) Delete Display Mapper entity.
11) UNDO deletion.
12) REDO deletion.
13) Look for errors and asserts.
:return: None
"""
@@ -133,21 +128,7 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, display_mapper_entity.exists())
# 5. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Test IsHidden.
display_mapper_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True)
# 7. Test IsVisible.
display_mapper_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True)
# 8. Set LDR color Grading LUT asset.
# 5. Set LDR color Grading LUT asset.
display_mapper_asset_path = os.path.join("TestData", "test.lightingpreset.azasset")
display_mapper_asset = Asset.find_asset_by_path(display_mapper_asset_path, False)
display_mapper_component.set_component_property_value(
@@ -157,19 +138,41 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper("LDR color Grading LUT")) == display_mapper_asset.id)
# 9. Delete Display Mapper entity.
# 6. Set Enable LDR color grading LUT property True
display_mapper_component.set_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True)
Report.result(
Tests.enable_ldr_color_grading_lut,
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True)
# 7. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 8. Test IsHidden.
display_mapper_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True)
# 9. Test IsVisible.
display_mapper_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True)
# 10. Delete Display Mapper entity.
display_mapper_entity.delete()
Report.result(Tests.entity_deleted, not display_mapper_entity.exists())
# 10. UNDO deletion.
# 11. UNDO deletion.
general.undo()
Report.result(Tests.deletion_undo, display_mapper_entity.exists())
# 11. REDO deletion.
# 12. REDO deletion.
general.redo()
Report.result(Tests.deletion_redo, not display_mapper_entity.exists())
# 12. Look for errors and asserts.
# 13. Look for errors and asserts.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
@@ -107,7 +107,6 @@ class EditorComponent:
return type_ids
def convert_to_azvector3(xyz) -> azlmbr.math.Vector3:
"""
Converts a vector3-like element into a azlmbr.math.Vector3
@@ -120,6 +119,7 @@ def convert_to_azvector3(xyz) -> azlmbr.math.Vector3:
else:
raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
class EditorEntity:
"""
Entity class is used to create and interact with Editor Entities.
@@ -470,3 +470,99 @@ class EditorEntity:
assert self.id.isValid(), "A valid entity id is required to focus on its owning prefab."
focus_prefab_result = azlmbr.prefab.PrefabFocusPublicRequestBus(bus.Broadcast, "FocusOnOwningPrefab", self.id)
assert focus_prefab_result.IsSuccess(), f"Prefab operation 'FocusOnOwningPrefab' failed. Error: {focus_prefab_result.GetError()}"
class EditorLevelEntity:
"""
EditorLevel class used to add and fetch level components.
Level entity is a special entity that you do not create/destroy independently of larger systems of level creation.
This collects a number of staticmethods that do not rely on entityId since Level entity is found internally by
EditorLevelComponentAPIBus requests.
"""
@staticmethod
def get_type_ids(component_names: list) -> list:
"""
Used to get type ids of given components list for EntityType Level
:param: component_names: List of components to get type ids
:return: List of type ids of given components.
"""
type_ids = editor.EditorComponentAPIBus(
bus.Broadcast, "FindComponentTypeIdsByEntityType", component_names, azlmbr.entity.EntityType().Level
)
return type_ids
@staticmethod
def add_component(component_name: str) -> EditorComponent:
"""
Used to add new component to Level.
:param component_name: String of component name to add.
:return: Component object of newly added component.
"""
component = EditorLevelEntity.add_components([component_name])[0]
return component
@staticmethod
def add_components(component_names: list) -> List[EditorComponent]:
"""
Used to add multiple components
:param: component_names: List of components to add to level
:return: List of newly added components to the level
"""
components = []
type_ids = EditorLevelEntity.get_type_ids(component_names)
for type_id in type_ids:
new_comp = EditorComponent()
new_comp.type_id = type_id
add_component_outcome = editor.EditorLevelComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", [type_id]
)
assert (
add_component_outcome.IsSuccess()
), f"Failure: Could not add component: '{new_comp.get_component_name()}' to level"
new_comp.id = add_component_outcome.GetValue()[0]
components.append(new_comp)
return components
@staticmethod
def get_components_of_type(component_names: list) -> List[EditorComponent]:
"""
Used to get components of type component_name that already exists on the level
:param component_names: List of names of components to check
:return: List of Level Component objects of given component name
"""
component_list = []
type_ids = EditorLevelEntity.get_type_ids(component_names)
for type_id in type_ids:
component = EditorComponent()
component.type_id = type_id
get_component_of_type_outcome = editor.EditorLevelComponentAPIBus(
bus.Broadcast, "GetComponentOfType", type_id
)
assert (
get_component_of_type_outcome.IsSuccess()
), f"Failure: Level does not have component:'{component.get_component_name()}'"
component.id = get_component_of_type_outcome.GetValue()
component_list.append(component)
return component_list
@staticmethod
def has_component(component_name: str) -> bool:
"""
Used to verify if the level has the specified component
:param component_name: Name of component to check for
:return: True, if level has specified component. Else, False
"""
type_ids = EditorLevelEntity.get_type_ids([component_name])
return editor.EditorLevelComponentAPIBus(bus.Broadcast, "HasComponentOfType", type_ids[0])
@staticmethod
def count_components_of_type(component_name: str) -> int:
"""
Used to get a count of the specified level component attached to the level
:param component_name: Name of component to check for
:return: integer count of occurences of level component attached to level or zero if none are present
"""
type_ids = EditorLevelEntity.get_type_ids([component_name])
return editor.EditorLevelComponentAPIBus(bus.Broadcast, "CountComponentsOfType", type_ids[0])
@@ -914,11 +914,11 @@ namespace UnitTest
m_testAssetManager->SetParallelDependentLoadingEnabled(true);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_LoadTest_SameAsset_DifferentFilters)
#else
TEST_F(AssetJobsFloodTest, LoadTest_SameAsset_DifferentFilters)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
@@ -1263,11 +1263,11 @@ namespace UnitTest
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded)
#else
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
// Setup has already created/destroyed assets
@@ -1304,11 +1304,11 @@ namespace UnitTest
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad)
#else
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
// Setup has already created/destroyed assets
@@ -1343,11 +1343,11 @@ namespace UnitTest
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed)
#else
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
// Setup has already created/destroyed assets
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.58087 1.8457C8.88135 1.8457 7.99903 2.19115 6.93392 2.88204L6.92893 2.87676C6.30965 3.2791 5.82426 3.79917 5.22899 4.43697L5.22899 4.43697C4.86895 4.82273 4.46872 5.25156 3.97436 5.72345L4.40762 6.13135C6.90029 8.44469 8.28286 9.60136 9.56289 9.60136C10.2624 9.60136 11.1447 9.25592 12.2098 8.56503L12.2148 8.57031C12.9369 8.10117 13.5717 7.47196 14.368 6.68268L14.3681 6.68264C14.6677 6.38563 14.9902 6.06596 15.3489 5.72362L14.9156 5.31571C12.4229 3.00237 10.8609 1.8457 9.58087 1.8457ZM7.6529 3.64381L7.65238 3.64325C8.14738 3.13093 8.82452 2.81518 9.57127 2.81518C11.0878 2.81518 12.3171 4.11733 12.3171 5.72362C12.3171 6.53856 12.0007 7.27522 11.4909 7.80326L11.4914 7.80381C10.9964 8.31614 10.3192 8.63189 9.57249 8.63189C8.05599 8.63189 6.82663 7.32973 6.82663 5.72345C6.82663 4.90851 7.14307 4.17185 7.6529 3.64381ZM9.56659 3.78466C9.05074 3.78466 8.58461 4.0095 8.25119 4.37149L8.25649 4.3771C7.93789 4.72586 7.74192 5.20047 7.74192 5.72345C7.74192 6.79431 8.56359 7.66241 9.57717 7.66241C10.093 7.66241 10.5592 7.43756 10.8926 7.07558L10.8873 7.06997C11.2059 6.72121 11.4018 6.24659 11.4018 5.72362C11.4018 4.65276 10.5802 3.78466 9.56659 3.78466ZM2.97474 1.99964H6.70318C6.07867 2.40803 5.53108 2.87237 5.07056 3.38303H2.97474C2.38778 3.38303 1.896 3.86007 1.896 4.44841V13.035C1.896 13.6233 2.37192 14.1003 2.97474 14.1003H11.5094C12.0964 14.1003 12.5881 13.6392 12.5881 13.035H12.6199V9.26317C13.1109 8.93674 13.5803 8.5478 14 8.10987V13.035C14 14.4025 12.8895 15.4996 11.5253 15.4996H2.97474C1.61046 15.4996 0.5 14.4025 0.5 13.035V4.44841C0.5 3.09681 1.61046 1.99964 2.97474 1.99964Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 4.60547C3 2.94862 4.34315 1.60547 6 1.60547C7.65685 1.60547 9 2.94862 9 4.60547V5.84076H10V6.60547C10 7.53544 10 8.00043 9.89778 8.38193C9.62038 9.4172 8.81173 10.2258 7.77646 10.5032C7.39496 10.6055 6.92997 10.6055 6 10.6055C5.07003 10.6055 4.60504 10.6055 4.22354 10.5032C3.18827 10.2258 2.37962 9.4172 2.10222 8.38193C2 8.00043 2 7.53544 2 6.60547V5.84076H3V4.60547ZM5.25 6.89953V9.54659H6.75V6.89953H5.25ZM6 2.66429C4.89543 2.66429 4 3.55972 4 4.66429V5.84076L8 5.84076V4.66429C8 3.55972 7.10457 2.66429 6 2.66429Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 690 B

@@ -7,7 +7,9 @@
<file alias="prefab.svg">Entity/prefab.svg</file>
<file alias="prefab_edit.svg">Entity/prefab_edit.svg</file>
<file alias="prefab_edit_open.svg">Entity/prefab_edit_open.svg</file>
<file alias="prefab_edit_open_readonly.svg">Entity/prefab_edit_open_readonly.svg</file>
<file alias="prefab_edit_close.svg">Entity/prefab_edit_close.svg</file>
<file alias="readonly.svg">Entity/readonly.svg</file>
</qresource>
<qresource prefix="/Level">
<file alias="level.svg">Level/level.svg</file>
@@ -14,7 +14,6 @@
#define AZ_TRAIT_UNIT_TEST_DILLER_TRIGGER_EVENT_COUNT 100000
#define AZ_TRAIT_DISABLE_FAILED_AP_CONNECTION_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS true
@@ -37,9 +37,9 @@ namespace AzToolsFramework
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
// ReadOnlyEntityPublicNotifications overrides ...
// ReadOnlyEntityPublicInterface overrides ...
bool IsReadOnly(const AZ::EntityId& entityId) override;
// ReadOnlyEntityQueryInterface overrides ...
void RefreshReadOnlyState(const EntityIdList& entityIds) override;
void RefreshReadOnlyStateForAllEntities() override;
@@ -50,7 +50,7 @@ namespace AzToolsFramework
return false;
}
void TraceLogger::PrepareLogFile(const AZStd::string& logFileName)
void TraceLogger::OpenLogFile(const AZStd::string& logFileName, bool clearLogFile)
{
using namespace AzFramework;
@@ -73,7 +73,7 @@ namespace AzToolsFramework
AZStd::string logPath;
StringFunc::Path::Join(logDirectory.c_str(), logFileName.c_str(), logPath);
m_logFile.reset(aznew LogFile(logPath.c_str()));
m_logFile.reset(aznew LogFile(logPath.c_str(), clearLogFile));
if (m_logFile)
{
m_logFile->SetMachineReadable(false);
@@ -81,7 +81,7 @@ namespace AzToolsFramework
{
m_logFile->AppendLog(LogFile::SEV_NORMAL, message.window.c_str(), message.message.c_str());
}
m_startupLogSink = {};
m_startupLogSink.clear();
m_logFile->FlushLog();
}
}
@@ -23,7 +23,7 @@ namespace AzToolsFramework
~TraceLogger();
//! Open log file and dump log sink into it
void PrepareLogFile(const AZStd::string& logFileName);
void OpenLogFile(const AZStd::string& logFileName, bool clearLogFile);
//! Add filter to ignore messages for windows with matching names
void AddWindowFilter(const AZStd::string& filter);
@@ -55,7 +55,8 @@ namespace AzToolsFramework
AZStd::string window;
AZStd::string message;
};
AZStd::vector<LogMessage> m_startupLogSink;
AZStd::list<LogMessage> m_startupLogSink;
AZStd::unordered_set<AZStd::string> m_windowFilters;
AZStd::unordered_set<AZStd::string> m_messageFilters;
AZStd::unique_ptr<AzFramework::LogFile> m_logFile;
@@ -12,6 +12,7 @@
#include <AzToolsFramework/ContainerEntity/ContainerEntityInterface.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusNotificationBus.h>
@@ -74,6 +75,13 @@ namespace AzToolsFramework::Prefab
"Prefab - PrefabFocusHandler - "
"Focus Mode Interface could not be found. "
"Check that it is being correctly initialized.");
m_readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get();
AZ_Assert(
m_readOnlyEntityQueryInterface,
"Prefab - PrefabFocusHandler - "
"ReadOnly Entity Query Interface could not be found. "
"Check that it is being correctly initialized.");
}
PrefabFocusOperationResult PrefabFocusHandler::FocusOnOwningPrefab(AZ::EntityId entityId)
@@ -186,6 +194,8 @@ namespace AzToolsFramework::Prefab
// Close all container entities in the old path.
CloseInstanceContainers(m_instanceFocusHierarchy);
AZ::EntityId previousContainerEntityId = m_focusedInstanceContainerEntityId;
// Do not store the container for the root instance, use an invalid EntityId instead.
m_focusedInstanceContainerEntityId = focusedInstance->get().GetParentInstance().has_value() ? focusedInstance->get().GetContainerEntityId() : AZ::EntityId();
m_focusedTemplateId = focusedInstance->get().GetTemplateId();
@@ -201,6 +211,12 @@ namespace AzToolsFramework::Prefab
m_focusModeInterface->SetFocusRoot(containerEntityId);
}
// Refresh the read-only cache, if the interface is initialized.
if (m_readOnlyEntityQueryInterface)
{
m_readOnlyEntityQueryInterface->RefreshReadOnlyState({ previousContainerEntityId, m_focusedInstanceContainerEntityId });
}
// Refresh path variables.
RefreshInstanceFocusList();
RefreshInstanceFocusPath();
@@ -22,6 +22,7 @@ namespace AzToolsFramework
{
class ContainerEntityInterface;
class FocusModeInterface;
class ReadOnlyEntityQueryInterface;
}
namespace AzToolsFramework::Prefab
@@ -93,6 +94,7 @@ namespace AzToolsFramework::Prefab
ContainerEntityInterface* m_containerEntityInterface = nullptr;
FocusModeInterface* m_focusModeInterface = nullptr;
InstanceEntityMapperInterface* m_instanceEntityMapperInterface = nullptr;
ReadOnlyEntityQueryInterface* m_readOnlyEntityQueryInterface = nullptr;
};
} // namespace AzToolsFramework::Prefab
@@ -918,6 +918,18 @@ namespace AzToolsFramework
}
}
bool PrefabPublicHandler::IsOwnedByProceduralPrefabInstance(AZ::EntityId entityId) const
{
if (InstanceOptionalReference instanceReference = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
instanceReference.has_value())
{
TemplateReference templateReference = m_prefabSystemComponentInterface->FindTemplate(instanceReference->get().GetTemplateId());
return (templateReference.has_value()) && (templateReference->get().IsProcedural());
}
return false;
}
bool PrefabPublicHandler::IsInstanceContainerEntity(AZ::EntityId entityId) const
{
InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entityId);
@@ -54,6 +54,7 @@ namespace AzToolsFramework
PrefabOperationResult GenerateUndoNodesForEntityChangeAndUpdateCache(AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) override;
bool IsOwnedByProceduralPrefabInstance(AZ::EntityId entityId) const override;
bool IsInstanceContainerEntity(AZ::EntityId entityId) const override;
bool IsLevelInstanceContainerEntity(AZ::EntityId entityId) const override;
AZ::EntityId GetInstanceContainerEntityId(AZ::EntityId entityId) const override;
@@ -101,6 +101,13 @@ namespace AzToolsFramework
*/
virtual PrefabOperationResult GenerateUndoNodesForEntityChangeAndUpdateCache(
AZ::EntityId entityId, UndoSystem::URSequencePoint* parentUndoBatch) = 0;
/**
* Detects if an entity is owned by a procedural prefab.
* @param entityId The entity to query.
* @return True if the entity is owned by a procedural prefab instance, false otherwise.
*/
virtual bool IsOwnedByProceduralPrefabInstance(AZ::EntityId entityId) const = 0;
/**
* Detects if an entity is the container entity for its owning prefab instance.
@@ -47,6 +47,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
#include <AzToolsFramework/FocusMode/FocusModeInterface.h>
#include <AzToolsFramework/ToolsComponents/ComponentAssetMimeDataContainer.h>
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
@@ -313,7 +314,7 @@ namespace AzToolsFramework
if (isEditorOnly)
{
return QIcon(QString(":/Icons/Entity_Editor_Only.svg"));
return QIcon(QString(":/Entity/entity_editoronly.svg"));
}
AZ::Entity* entity = nullptr;
@@ -322,10 +323,10 @@ namespace AzToolsFramework
if (!isInitiallyActive)
{
return QIcon(QString(":/Icons/Entity_Not_Active.svg"));
return QIcon(QString(":/Entity/entity_notactive.svg"));
}
return QIcon(QString(":/Icons/Entity.svg"));
return QIcon(QString(":/Entity/entity.svg"));
}
QVariant EntityOutlinerListModel::GetEntityTooltip(const AZ::EntityId& id) const
@@ -1994,9 +1995,13 @@ namespace AzToolsFramework
, m_lockCheckBoxes(parent, "Lock", EntityOutlinerListModel::PartiallyLockedRole, EntityOutlinerListModel::LockedAncestorRole)
{
m_editorEntityFrameworkInterface = AZ::Interface<AzToolsFramework::EditorEntityUiInterface>::Get();
AZ_Assert((m_editorEntityFrameworkInterface != nullptr),
"EntityOutlinerItemDelegate requires a EditorEntityFrameworkInterface instance on Construction.");
m_readOnlyEntityPublicInterface = AZ::Interface<AzToolsFramework::ReadOnlyEntityPublicInterface>::Get();
AZ_Assert(
(m_readOnlyEntityPublicInterface != nullptr),
"EntityOutlinerItemDelegate requires a ReadOnlyEntityPublicInterface instance on Construction.");
}
EntityOutlinerItemDelegate::CheckboxGroup::CheckboxGroup(QWidget* parent, AZStd::string prefix,
@@ -2108,6 +2113,12 @@ namespace AzToolsFramework
}
PaintEntityNameAsRichText(painter, customOption, index);
// Paint Read-Only icon if necessary
if (m_readOnlyEntityPublicInterface->IsReadOnly(entityId))
{
PaintReadOnlyIcon(painter, option, index);
}
}
break;
default:
@@ -2166,10 +2177,10 @@ namespace AzToolsFramework
backgroundPath.addRect(backgroundRect);
QColor backgroundColor = m_hoverColor;
QColor backgroundColor = s_hoverColor;
if (isSelected)
{
backgroundColor = m_selectedColor;
backgroundColor = s_selectedColor;
}
painter->fillPath(backgroundPath, backgroundColor);
@@ -2336,6 +2347,20 @@ namespace AzToolsFramework
EntityOutlinerListModel::s_paintingName = false;
}
void EntityOutlinerItemDelegate::PaintReadOnlyIcon(QPainter* painter, const QStyleOptionViewItem& option, [[maybe_unused]] const QModelIndex& index) const
{
// Build the rect that will be used to paint the icon
QRect readOnlyRect = QRect(option.rect.topLeft() + s_readOnlyOffset, QSize(s_readOnlyRadius * 2, s_readOnlyRadius * 2));
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(Qt::NoPen);
painter->setBrush(s_readOnlyBackgroundColor);
painter->drawEllipse(readOnlyRect.center(), s_readOnlyRadius, s_readOnlyRadius);
s_readOnlyIcon.paint(painter, readOnlyRect);
painter->restore();
}
QSize EntityOutlinerItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
{
// Get the height of a tall character...
@@ -38,6 +38,7 @@ namespace AzToolsFramework
{
class EditorEntityUiInterface;
class FocusModeInterface;
class ReadOnlyEntityPublicInterface;
namespace EntityOutliner
{
@@ -344,6 +345,9 @@ namespace AzToolsFramework
// Paint the entity name using rich text
void PaintEntityNameAsRichText(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
// Paint the read-only icon on the entity
void PaintReadOnlyIcon(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
struct CheckboxGroup
{
EntityOutlinerCheckBox m_default;
@@ -372,10 +376,17 @@ namespace AzToolsFramework
// this is a cache, and is hence mutable
mutable QRect m_cachedBoundingRectOfTallCharacter;
const QColor m_selectedColor = QColor(255, 255, 255, 45);
const QColor m_hoverColor = QColor(255, 255, 255, 30);
inline static const QColor s_selectedColor = QColor(255, 255, 255, 45);
inline static const QColor s_hoverColor = QColor(255, 255, 255, 30);
inline static const QColor s_readOnlyBackgroundColor = QColor("#444444");
inline static const QPoint s_readOnlyOffset = QPoint(10, 10);
inline static const int s_readOnlyRadius = 6;
QIcon s_readOnlyIcon = QIcon(QString(":/Entity/readonly.svg"));
EditorEntityUiInterface* m_editorEntityFrameworkInterface = nullptr;
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
};
}
@@ -27,6 +27,7 @@
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/EditorPrefabComponent.h>
#include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
#include <AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
@@ -1264,7 +1265,14 @@ namespace AzToolsFramework
}
else
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
if (s_prefabPublicInterface->IsOwnedByProceduralPrefabInstance(entityId))
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_proceduralPrefabUiHandler.GetHandlerId());
}
else
{
s_editorEntityUiInterface->RegisterEntity(entityId, m_prefabUiHandler.GetHandlerId());
}
// Register entity as a container
s_containerEntityInterface->RegisterEntityAsContainer(entityId);
@@ -18,10 +18,11 @@
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/UI/Prefab/LevelRootUiHandler.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationBus.h>
#include <AzToolsFramework/UI/Prefab/PrefabIntegrationInterface.h>
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
#include <AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.h>
#include <AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.h>
#include <AzQtComponents/Components/Widgets/Card.h>
@@ -92,12 +93,18 @@ namespace AzToolsFramework
void ExecuteSavePrefabDialog(TemplateId templateId, bool useSaveAllPrefabsPreference) override;
private:
// Used to handle the UI for the level root
// Used to handle the UI for the level root.
LevelRootUiHandler m_levelRootUiHandler;
// Used to handle the UI for prefab entities
// Used to handle the UI for prefab entities.
PrefabUiHandler m_prefabUiHandler;
// Used to handle the UI for procedural prefab entities.
ProceduralPrefabUiHandler m_proceduralPrefabUiHandler;
// Ensures entities owned by procedural prefab instances are marked as read-only correctly.
ProceduralPrefabReadOnlyHandler m_proceduralPrefabReadOnlyHandler;
// Context menu item handlers
static void ContextMenu_CreatePrefab(AzToolsFramework::EntityIdList selectedEntities);
static void ContextMenu_InstantiatePrefab();
@@ -23,17 +23,6 @@ namespace AzToolsFramework
{
AzFramework::EntityContextId PrefabUiHandler::s_editorEntityContextId = AzFramework::EntityContextId::CreateNull();
const QColor PrefabUiHandler::m_backgroundColor = QColor("#444444");
const QColor PrefabUiHandler::m_backgroundHoverColor = QColor("#5A5A5A");
const QColor PrefabUiHandler::m_backgroundSelectedColor = QColor("#656565");
const QColor PrefabUiHandler::m_prefabCapsuleColor = QColor("#1E252F");
const QColor PrefabUiHandler::m_prefabCapsuleDisabledColor = QColor("#35383C");
const QColor PrefabUiHandler::m_prefabCapsuleEditColor = QColor("#4A90E2");
const QString PrefabUiHandler::m_prefabIconPath = QString(":/Entity/prefab.svg");
const QString PrefabUiHandler::m_prefabEditIconPath = QString(":/Entity/prefab_edit.svg");
const QString PrefabUiHandler::m_prefabEditOpenIconPath = QString(":/Entity/prefab_edit_open.svg");
const QString PrefabUiHandler::m_prefabEditCloseIconPath = QString(":/Entity/prefab_edit_close.svg");
PrefabUiHandler::PrefabUiHandler()
{
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
@@ -46,7 +46,7 @@ namespace AzToolsFramework
void OnOutlinerItemCollapse(const QModelIndex& index) const override;
bool OnEntityDoubleClick(AZ::EntityId entityId) const override;
private:
protected:
Prefab::PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
@@ -56,17 +56,17 @@ namespace AzToolsFramework
static AzFramework::EntityContextId s_editorEntityContextId;
static constexpr int m_prefabCapsuleRadius = 6;
static constexpr int m_prefabBorderThickness = 2;
static const QColor m_backgroundColor;
static const QColor m_backgroundHoverColor;
static const QColor m_backgroundSelectedColor;
static const QColor m_prefabCapsuleColor;
static const QColor m_prefabCapsuleDisabledColor;
static const QColor m_prefabCapsuleEditColor;
static const QString m_prefabIconPath;
static const QString m_prefabEditIconPath;
static const QString m_prefabEditOpenIconPath;
static const QString m_prefabEditCloseIconPath;
int m_prefabCapsuleRadius = 6;
int m_prefabBorderThickness = 2;
QColor m_backgroundColor = QColor("#444444");
QColor m_backgroundHoverColor = QColor("#5A5A5A");
QColor m_backgroundSelectedColor = QColor("#656565");
QColor m_prefabCapsuleColor = QColor("#1E252F");
QColor m_prefabCapsuleDisabledColor = QColor("#35383C");
QColor m_prefabCapsuleEditColor = QColor("#4A90E2");
QString m_prefabIconPath = QString(":/Entity/prefab.svg");
QString m_prefabEditIconPath = QString(":/Entity/prefab_edit.svg");
QString m_prefabEditOpenIconPath = QString(":/Entity/prefab_edit_open.svg");
QString m_prefabEditCloseIconPath = QString(":/Entity/prefab_edit_close.svg");
};
} // namespace AzToolsFramework
@@ -0,0 +1,68 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
namespace AzToolsFramework
{
namespace Prefab
{
ProceduralPrefabReadOnlyHandler::ProceduralPrefabReadOnlyHandler()
{
m_prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
AZ_Assert(
m_prefabPublicInterface != nullptr,
"ProceduralPrefabReadOnlyHandler requires a PrefabPublicInterface instance on Initialize.");
m_prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
AZ_Assert(
m_prefabFocusPublicInterface != nullptr,
"ProceduralPrefabReadOnlyHandler requires a PrefabFocusPublicInterface instance on Initialize.");
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
ReadOnlyEntityQueryRequestBus::Handler::BusConnect(editorEntityContextId);
// Refresh the whole read-only cache
if (auto readOnlyEntityQueryInterface = AZ::Interface<ReadOnlyEntityQueryInterface>::Get())
{
readOnlyEntityQueryInterface->RefreshReadOnlyStateForAllEntities();
}
}
ProceduralPrefabReadOnlyHandler ::~ProceduralPrefabReadOnlyHandler()
{
ReadOnlyEntityQueryRequestBus::Handler::BusDisconnect();
}
void ProceduralPrefabReadOnlyHandler::IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly)
{
if(m_prefabPublicInterface->IsOwnedByProceduralPrefabInstance(entityId))
{
// All entities nested inside a procedural prefabs should always be marked as read-only.
if (!m_prefabPublicInterface->IsInstanceContainerEntity(entityId))
{
isReadOnly = true;
}
// The container entity of a procedural prefab should only be marked as read-only when the prefab is being edited.
if (m_prefabFocusPublicInterface->IsOwningPrefabBeingFocused(entityId))
{
isReadOnly = true;
}
}
}
} // namespace Prefab
} // namespace AzToolsFramework
@@ -0,0 +1,43 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityBus.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabFocusPublicInterface;
class PrefabPublicInterface;
//! Ensures entities in a procedural prefab are correctly reported as read-only.
class ProceduralPrefabReadOnlyHandler
: public ReadOnlyEntityQueryRequestBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(ProceduralPrefabReadOnlyHandler, AZ::SystemAllocator, 0);
AZ_RTTI(AzToolsFramework::ProceduralPrefabReadOnlyHandler, "{A2D72461-8CA3-45EE-81D2-4976BC0B6AE9}");
ProceduralPrefabReadOnlyHandler();
~ProceduralPrefabReadOnlyHandler() override;
// ReadOnlyEntityQueryRequestBus overrides ...
void IsReadOnly(const AZ::EntityId& entityId, bool& isReadOnly) override;
private:
PrefabPublicInterface* m_prefabPublicInterface = nullptr;
PrefabFocusPublicInterface* m_prefabFocusPublicInterface = nullptr;
};
} // namespace Prefab
} // namespace AzToolsFramework
@@ -0,0 +1,32 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzToolsFramework/UI/Prefab/Procedural/ProceduralPrefabUiHandler.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
namespace AzToolsFramework
{
ProceduralPrefabUiHandler::ProceduralPrefabUiHandler()
{
m_prefabCapsuleColor = QColor("#361561");
m_prefabCapsuleDisabledColor = QColor("#4B3455");
m_prefabCapsuleEditColor = QColor("#361561");
m_prefabIconPath = QString(":/Entity/prefab_edit.svg");
m_prefabEditOpenIconPath = QString(":/Entity/prefab_edit_open_readonly.svg");
}
QString ProceduralPrefabUiHandler::GenerateItemTooltip(AZ::EntityId entityId) const
{
if (AZ::IO::Path path = m_prefabPublicInterface->GetOwningInstancePrefabPath(entityId); !path.empty())
{
return QObject::tr("Double click to inspect.\n%1").arg(path.Native().data());
}
return QString();
}
}
@@ -0,0 +1,36 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzToolsFramework/UI/Prefab/PrefabUiHandler.h>
#include <AzFramework/Entity/EntityContextBus.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabFocusPublicInterface;
class PrefabPublicInterface;
};
//! Implements the Editor UI for Procedural Prefabs.
class ProceduralPrefabUiHandler
: public PrefabUiHandler
{
public:
AZ_CLASS_ALLOCATOR(ProceduralPrefabUiHandler, AZ::SystemAllocator, 0);
AZ_RTTI(AzToolsFramework::ProceduralPrefabUiHandler, "{3A3DF9FF-9C2E-4439-B7B4-72173B5A3502}", PrefabUiHandler);
ProceduralPrefabUiHandler();
~ProceduralPrefabUiHandler() override = default;
QString GenerateItemTooltip(AZ::EntityId entityId) const override;
};
} // namespace AzToolsFramework
@@ -45,6 +45,7 @@ AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/AssetBrowser/EBusFindAssetTypeByName.h>
#include <AzToolsFramework/ComponentMode/ComponentModeDelegate.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Slice/SliceDataFlagsCommand.h>
@@ -497,6 +498,9 @@ namespace AzToolsFramework
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
AZ_Assert(m_prefabPublicInterface != nullptr, "EntityPropertyEditor requires a PrefabPublicInterface instance on Initialize.");
m_readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get();
AZ_Assert(m_readOnlyEntityPublicInterface != nullptr, "EntityPropertyEditor requires a ReadOnlyEntityPublicInterface instance on Initialize.");
setObjectName("EntityPropertyEditor");
setAcceptDrops(true);
@@ -535,10 +539,6 @@ namespace AzToolsFramework
model->setItem(row, 0, m_comboItems[row]);
}
m_gui->m_statusComboBox->setModel(model);
m_gui->m_statusComboBox->setStyleSheet("QComboBox {border: 0px; border-radius:3px; background-color:#555555; color:white}"
"QComboBox:on {background-color:#e9e9e9; color:black; border:0px}"
"QComboBox::down-arrow:on {image: url(:/stylesheet/img/dropdowns/black_down_arrow.png)}"
"QComboBox::drop-down {border-radius: 3p}");
AzQtComponents::ComboBox::addCustomCheckStateStyle(m_gui->m_statusComboBox);
EnableEditor(true);
m_sceneIsNew = true;
@@ -565,6 +565,12 @@ namespace AzToolsFramework
AZ::EntitySystemBus::Handler::BusConnect();
EntityPropertyEditorRequestBus::Handler::BusConnect();
EditorWindowUIRequestBus::Handler::BusConnect();
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(
editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
ReadOnlyEntityPublicNotificationBus::Handler::BusConnect(editorEntityContextId);
m_spacer = nullptr;
m_emptyIcon = QIcon();
@@ -614,6 +620,7 @@ namespace AzToolsFramework
{
qApp->removeEventFilter(this);
ReadOnlyEntityPublicNotificationBus::Handler::BusDisconnect();
EditorWindowUIRequestBus::Handler::BusDisconnect();
EntityPropertyEditorRequestBus::Handler::BusDisconnect();
ToolsApplicationEvents::Bus::Handler::BusDisconnect();
@@ -973,7 +980,7 @@ namespace AzToolsFramework
m_gui->m_entityDetailsLabel->setVisible(false);
// If we're in edit mode, make the name field editable.
m_gui->m_entityNameEditor->setReadOnly(!m_gui->m_componentListContents->isEnabled());
m_gui->m_entityNameEditor->setReadOnly(!m_gui->m_componentListContents->isEnabled() || m_selectionContainsReadOnlyEntity);
// get the name of the entity.
auto entity = GetSelectedEntityById(entityId);
@@ -1062,6 +1069,12 @@ namespace AzToolsFramework
bool EntityPropertyEditor::CanAddComponentsToSelection(const SelectionEntityTypeInfo& selectionEntityTypeInfo) const
{
if (m_selectionContainsReadOnlyEntity)
{
// Can't add components if there is a read only entity in the selection
return false;
}
if (selectionEntityTypeInfo == SelectionEntityTypeInfo::Mixed ||
selectionEntityTypeInfo == SelectionEntityTypeInfo::None)
{
@@ -1126,6 +1139,17 @@ namespace AzToolsFramework
m_selectedEntityIds.clear();
GetSelectedEntities(m_selectedEntityIds);
// Check if any of the selected entities are marked as read only
m_selectionContainsReadOnlyEntity = false;
for (const auto& entityId : m_selectedEntityIds)
{
if (m_readOnlyEntityPublicInterface->IsReadOnly(entityId))
{
m_selectionContainsReadOnlyEntity = true;
break;
}
}
SourceControlFileInfo scFileInfo;
ToolsApplicationRequests::Bus::BroadcastResult(scFileInfo, &ToolsApplicationRequests::GetSceneSourceControlInfo);
@@ -1681,6 +1705,12 @@ namespace AzToolsFramework
componentEditor->UpdateExpandability();
componentEditor->InvalidateAll(!componentInFilter ? m_filterString.c_str() : nullptr);
// If we are in read only mode, then show the components as disabled
if (m_selectionContainsReadOnlyEntity)
{
componentEditor->mockDisabledState(true);
}
if (!componentEditor->GetPropertyEditor()->HasFilteredOutNodes() || componentEditor->GetPropertyEditor()->HasVisibleNodes())
{
for (AZ::Component* componentInstance : componentInstances)
@@ -3077,6 +3107,7 @@ namespace AzToolsFramework
}
}
m_gui->m_statusComboBox->setDisabled(m_selectionContainsReadOnlyEntity);
m_gui->m_statusComboBox->setVisible(!m_isSystemEntityEditor && !m_isLevelEntityEditor);
m_gui->m_statusComboBox->style()->unpolish(m_gui->m_statusComboBox);
m_gui->m_statusComboBox->style()->polish(m_gui->m_statusComboBox);
@@ -3304,7 +3335,8 @@ namespace AzToolsFramework
const auto& componentsToEdit = GetSelectedComponents();
const bool hasComponents = !m_selectedEntityIds.empty() && !componentsToEdit.empty();
const bool allowRemove = hasComponents && AreComponentsRemovable(componentsToEdit);
// Don't allow components to be removed/cut/enabled/disabled if read only
const bool allowRemove = hasComponents && AreComponentsRemovable(componentsToEdit) && !m_selectionContainsReadOnlyEntity;
const bool allowCopy = hasComponents && AreComponentsCopyable(componentsToEdit);
m_actionToDeleteComponents->setEnabled(allowRemove);
@@ -3366,6 +3398,12 @@ namespace AzToolsFramework
return false;
}
if (m_selectionContainsReadOnlyEntity)
{
// Can't paste components if there is a read only entity in the selection
return false;
}
// Grab component data from clipboard, if exists
const QMimeData* mimeData = ComponentMimeData::GetComponentMimeDataFromClipboard();
@@ -5727,6 +5765,14 @@ namespace AzToolsFramework
SaveComponentEditorState();
}
void EntityPropertyEditor::OnReadOnlyEntityStatusChanged(const AZ::EntityId& entityId, [[maybe_unused]] bool readOnly)
{
if (IsEntitySelected(entityId))
{
UpdateContents();
}
}
void EntityPropertyEditor::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
@@ -29,6 +29,7 @@
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityBus.h>
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponentBus.h>
#include <AzQtComponents/Components/O3DEStylesheet.h>
@@ -62,6 +63,7 @@ namespace AzToolsFramework
class ComponentPaletteWidget;
class ComponentModeCollectionInterface;
struct SourceControlFileInfo;
class ReadOnlyEntityPublicInterface;
namespace AssetBrowser
{
@@ -116,6 +118,7 @@ namespace AzToolsFramework
, public AZ::EntitySystemBus::Handler
, public AZ::TickBus::Handler
, private EditorWindowUIRequestBus::Handler
, private ReadOnlyEntityPublicNotificationBus::Handler
{
Q_OBJECT;
public:
@@ -253,6 +256,9 @@ namespace AzToolsFramework
// EditorWindowRequestBus overrides
void SetEditorUiEnabled(bool enable) override;
// ReadOnlyEntityPublicNotificationBus overrides ...
void OnReadOnlyEntityStatusChanged(const AZ::EntityId& entityId, bool readOnly) override;
bool IsEntitySelected(const AZ::EntityId& id) const;
bool IsSingleEntitySelected(const AZ::EntityId& id) const;
@@ -623,6 +629,9 @@ namespace AzToolsFramework
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
bool m_prefabsAreEnabled = false;
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
bool m_selectionContainsReadOnlyEntity = false;
// Reordering row widgets within the RPE.
static constexpr float MoveFadeSeconds = 0.5f;
@@ -191,7 +191,7 @@
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color:rgb(51, 51, 51)</string>
<string notr="true">QWidget#m_darkBox { background-color:rgb(51, 51, 51) }</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
@@ -444,6 +444,9 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="styleSheet">
<string notr="true">background-color:rgb(51, 51, 51)</string>
</property>
</widget>
</item>
<item>
@@ -768,6 +768,10 @@ set(FILES
UI/Prefab/PrefabUiHandler.cpp
UI/Prefab/PrefabViewportFocusPathHandler.h
UI/Prefab/PrefabViewportFocusPathHandler.cpp
UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.h
UI/Prefab/Procedural/ProceduralPrefabReadOnlyHandler.cpp
UI/Prefab/Procedural/ProceduralPrefabUiHandler.h
UI/Prefab/Procedural/ProceduralPrefabUiHandler.cpp
UI/Notifications/ToastNotificationsView.cpp
UI/Notifications/ToastNotificationsView.h
UI/Notifications/ToastBus.h
@@ -96,4 +96,22 @@ namespace AzToolsFramework
// Verify the child entity is no longer marked as read-only
EXPECT_FALSE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
}
TEST_F(ReadOnlyEntityFixture, EnsureCacheIsClearedCorrectlyEvenIfUnchanged)
{
// Create a handler that sets all entities to read-only.
ReadOnlyHandlerAlwaysTrue alwaysTrueHandler;
{
// Create a handler that sets the child entity to read-only.
ReadOnlyHandlerEntityId entityIdHandler(m_entityMap[ChildEntityName]);
// Verify the child entity is marked as read-only
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
}
// When the handler goes out of scope, it calls RefreshReadOnlyStateForAllEntities and refreshes the cache.
// Verify the child entity is still marked as read-only
EXPECT_TRUE(m_readOnlyEntityPublicInterface->IsReadOnly(m_entityMap[ChildEntityName]));
}
}
@@ -299,7 +299,7 @@ void android_main(android_app* appState)
{
// Adding a start up banner so you can see when the game is starting up in amongst the logcat spam
LOGI("****************************************************************");
LOGI("* Amazon Lumberyard - Launching Game... *");
LOGI("* Launching Game... *");
LOGI("****************************************************************");
// setup the system command handler which are guaranteed to be called on the same
@@ -175,7 +175,8 @@ namespace AtomToolsFramework
Base::StartCommon(systemEntity);
m_traceLogger.PrepareLogFile(GetBuildTargetName() + ".log");
const bool clearLogFile = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false);
m_traceLogger.OpenLogFile(GetBuildTargetName() + ".log", clearLogFile);
AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusConnect();
AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(
@@ -15,39 +15,39 @@ namespace GraphCanvas
void AddEntryToDatabase(const AZStd::string& baseKey, const AZStd::string& name, const rapidjson::Value& it, TranslationFormat* translationFormat)
{
AZStd::string finalKey = baseKey;
if (it.IsString())
{
if (translationFormat->m_database.find(finalKey) == translationFormat->m_database.end())
auto translationDbItr = translationFormat->m_database.find(baseKey);
if (translationDbItr == translationFormat->m_database.end())
{
translationFormat->m_database[finalKey] = it.GetString();
translationFormat->m_database[baseKey] = it.GetString();
}
else
{
AZStd::string existingValue = translationFormat->m_database[finalKey.c_str()];
const AZStd::string& existingValue = translationDbItr->second;
// There is a name collision
AZStd::string error = AZStd::string::format("Unable to store key: %s with value: %s because that key already exists with value: %s (proposed: %s)", finalKey.c_str(), it.GetString(), existingValue.c_str(), it.GetString());
const AZStd::string error = AZStd::string::format("Unable to store key: %s with value: %s because that key already exists with value: %s (proposed: %s)", baseKey.c_str(), it.GetString(), existingValue.c_str(), it.GetString());
AZ_Error("TranslationSerializer", false, error.c_str());
}
}
else if (it.IsObject())
{
AZStd::string finalKey = baseKey;
if (!name.empty())
{
finalKey.append(".");
finalKey.append(name);
}
AZStd::string itemKey = finalKey;
AZStd::string itemKey;
for (auto objIt = it.MemberBegin(); objIt != it.MemberEnd(); ++objIt)
{
itemKey = finalKey;
itemKey.append(".");
itemKey.append(objIt->name.GetString());
AddEntryToDatabase(itemKey, name, objIt->value, translationFormat);
itemKey = finalKey;
}
}
@@ -60,18 +60,21 @@ namespace GraphCanvas
key.append(name);
}
AZStd::string itemKey = key;
AZStd::string itemKey;
const rapidjson::Value& array = it;
for (rapidjson::SizeType i = 0; i < array.Size(); ++i)
{
itemKey = key;
// if there is a "base" member within the object, then use it, otherwise use the index
if (array[i].IsObject())
const auto& element = array[i];
if (element.IsObject())
{
if (array[i].HasMember(Schema::Field::key))
rapidjson::Value::ConstMemberIterator innerKeyItr = element.FindMember(Schema::Field::key);
if (innerKeyItr != element.MemberEnd())
{
AZStd::string innerKey = array[i].FindMember(Schema::Field::key)->value.GetString();
itemKey.append(AZStd::string::format(".%s", innerKey.c_str()));
itemKey.append(AZStd::string::format(".%s", innerKeyItr->value.GetString()));
}
else
{
@@ -79,9 +82,7 @@ namespace GraphCanvas
}
}
AddEntryToDatabase(itemKey, "", array[i], translationFormat);
itemKey = key;
AddEntryToDatabase(itemKey, "", element, translationFormat);
}
}
}
@@ -114,42 +115,32 @@ namespace GraphCanvas
{
const rapidjson::Value::ConstMemberIterator entries = inputValue.FindMember(Schema::Field::entries);
AZStd::string keyStr;
AZStd::string contextStr;
AZStd::string variantStr;
AZStd::string baseKey;
rapidjson::SizeType entryCount = entries->value.Size();
for (rapidjson::SizeType i = 0; i < entryCount; ++i)
{
const rapidjson::Value& entry = entries->value[i];
AZStd::string keyStr;
rapidjson::Value::ConstMemberIterator keyValue;
if (entry.HasMember(Schema::Field::key))
{
keyValue = entry.FindMember(Schema::Field::key);
keyStr = keyValue->value.GetString();
}
rapidjson::Value::ConstMemberIterator keyItr = entry.FindMember(Schema::Field::key);
keyStr = keyItr != entry.MemberEnd() ? keyItr->value.GetString() : "";
AZStd::string contextStr;
rapidjson::Value::ConstMemberIterator contextValue;
if (entry.HasMember(Schema::Field::context))
{
contextValue = entry.FindMember(Schema::Field::context);
contextStr = contextValue->value.GetString();
}
rapidjson::Value::ConstMemberIterator contextItr = entry.FindMember(Schema::Field::context);
contextStr = contextItr != entry.MemberEnd() ? contextItr->value.GetString() : "";
AZStd::string variantStr;
rapidjson::Value::ConstMemberIterator variantValue;
if (entry.HasMember(Schema::Field::variant))
{
variantValue = entry.FindMember(Schema::Field::variant);
variantStr = variantValue->value.GetString();
}
rapidjson::Value::ConstMemberIterator variantItr = entry.FindMember(Schema::Field::variant);
variantStr = variantItr != entry.MemberEnd() ? variantItr->value.GetString() : "";
AZStd::string baseKey = contextStr;
if (keyStr.empty())
{
AZ_Error("TranslationDatabase", false, "Every entry in the Translation data must have a key: %s", baseKey.c_str());
AZ_Error("TranslationDatabase", false, "Every entry in the Translation data must have a key: %s", contextStr.c_str());
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, "Every entry in the Translation data must have a key");
}
baseKey = contextStr;
if (!baseKey.empty())
{
baseKey.append(".");
@@ -167,7 +158,7 @@ namespace GraphCanvas
for (auto it = entry.MemberBegin(); it != entry.MemberEnd(); ++it)
{
// Skip the fixed elements
if (it == keyValue || it == contextValue || it == variantValue)
if (it == keyItr || it == contextItr || it == variantItr)
{
continue;
}
+1 -1
View File
@@ -411,7 +411,7 @@ def register_project_path(json_data: dict,
if not remove:
# registering a project has the additional step of setting the project.json 'engine' field
this_engine_json = manifest.get_engine_json_data(engine_path=manifest.get_this_engine_path())
this_engine_json = manifest.get_engine_json_data(engine_path=engine_path if engine_path else manifest.get_this_engine_path())
if not this_engine_json:
return 1
project_json_data = manifest.get_project_json_data(project_path=project_path)