diff --git a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py index 0060a3b29a..bedf474387 100644 --- a/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py +++ b/AutomatedTesting/Gem/PythonTests/editor/EditorScripts/Menus_FileMenuOptions.py @@ -38,7 +38,7 @@ def Menus_FileMenuOptions_Work(): ("Save As",), ("Save Level Statistics",), ("Edit Project Settings",), - #("Edit Platform Settings",), Temporarily disabled due to https://github.com/o3de/o3de/issues/6604 + ("Edit Platform Settings",), ("New Project",), ("Open Project",), ("Show Log File",), diff --git a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas index 3869dfdfcb..5d61fea3e7 100644 --- a/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas +++ b/AutomatedTesting/Levels/Multiplayer/AutoComponent_RPC/AutoComponent_RPC_NetLevelEntity.scriptcanvas @@ -5,15 +5,118 @@ "ClassData": { "m_scriptCanvas": { "Id": { - "id": 1685762441320719908 + "id": 7369225496155711251 }, "Name": "AutoComponent_RPC_NetLevelEntity", "Components": { "Component_[2936040539888065977]": { - "$type": "{4D755CA9-AB92-462C-B24F-0B3376F19967} Graph", + "$type": "EditorGraph", "Id": 2936040539888065977, "m_graphData": { "m_nodes": [ + { + "Id": { + "id": 11993350262154 + }, + "Name": "SC-Node(GetAuthorityToClientNoParams_PlayFxEventByEntityId)", + "Components": { + "Component_[103174496050601676]": { + "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", + "Id": 103174496050601676, + "Slots": [ + { + "id": { + "m_id": "{AE2A0AA3-99DD-4DE4-AFEA-7560F078943C}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "EntityId: 0", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{4D460DFD-82A1-4A92-8EAE-D32A96E79FA0}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{3FD9A3D0-FFAA-475E-89CB-D24EB4FEB952}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{F120FEE1-448B-4D61-8439-10511C797707}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Event<>", + "DisplayDataType": { + "m_type": 4, + "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}" + }, + "Descriptor": { + "ConnectionType": 2, + "SlotType": 2 + }, + "DataType": 1 + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 1 + }, + "isNullPointer": false, + "$type": "EntityId", + "value": { + "id": 2901262558 + }, + "label": "EntityId: 0" + } + ], + "methodType": 2, + "methodName": "GetAuthorityToClientNoParams_PlayFxEventByEntityId", + "className": "NetworkTestLevelEntityComponent", + "inputSlots": [ + { + "m_id": "{AE2A0AA3-99DD-4DE4-AFEA-7560F078943C}" + } + ], + "prettyClassName": "NetworkTestLevelEntityComponent" + } + } + }, { "Id": { "id": 56986727032248 @@ -338,7 +441,7 @@ "isNullPointer": false, "$type": "{03AAAB3F-5C47-5A66-9EBC-D5FA4DB353C9} AZStd::string", "value": "AutoComponent_RPC_NetLevelEntity: I'm a client playing some superficial fx", - "label": "Color" + "label": "Text" }, { "scriptCanvasType": { @@ -352,7 +455,7 @@ 0.0, 1.0 ], - "label": "Color: 2" + "label": "Color" }, { "scriptCanvasType": { @@ -361,7 +464,7 @@ "isNullPointer": false, "$type": "double", "value": 2.0, - "label": "Number: 3" + "label": "Duration" } ], "methodType": 0, @@ -486,153 +589,6 @@ } } }, - { - "Id": { - "id": 8318619017825 - }, - "Name": "SC-EventNode(AuthorityToClientNoParams_PlayFx Notify Event)", - "Components": { - "Component_[15772128920819427182]": { - "$type": "AzEventHandler", - "Id": 15772128920819427182, - "Slots": [ - { - "id": { - "m_id": "{2A42C379-8E3B-46EF-BC63-C1D5395CB583}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - }, - { - "$type": "ConnectionLimitContract", - "limit": 1 - }, - { - "$type": "RestrictedNodeContract", - "m_nodeId": { - "id": 7820402811489 - } - } - ], - "slotName": "Connect", - "toolTip": "Connect the AZ Event to this AZ Event Handler.", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{3DB9829E-6088-49B9-A56D-4D1884C679BD}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "Disconnect", - "toolTip": "Disconnect current AZ Event from this AZ Event Handler.", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{9AC3CF57-B648-4B43-8FCA-8576B6EA350B}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "On Connected", - "toolTip": "Signaled when a connection has taken place.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{5FB8D529-6FC6-4543-99B5-5B147EBD7BE6}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "On Disconnected", - "toolTip": "Signaled when this event handler is disconnected.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{0481BBFE-D31E-421F-A6C2-8A7AF3012545}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "OnEvent", - "toolTip": "Triggered when the AZ Event invokes Signal() function.", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - }, - "IsLatent": true - }, - { - "id": { - "m_id": "{5F809F1C-ED4E-4391-9E33-AD3B64561A40}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - }, - { - "$type": "ConnectionLimitContract", - "limit": 1 - }, - { - "$type": "RestrictedNodeContract", - "m_nodeId": { - "id": 7820402811489 - } - } - ], - "slotName": "AuthorityToClientNoParams_PlayFx Notify Event", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 2 - }, - "DataType": 1 - } - ], - "Datums": [ - { - "scriptCanvasType": { - "m_type": 4, - "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}" - }, - "isNullPointer": true, - "label": "AuthorityToClientNoParams_PlayFx Notify Event" - } - ], - "m_azEventEntry": { - "m_eventName": "AuthorityToClientNoParams_PlayFx Notify Event", - "m_eventSlotId": { - "m_id": "{5F809F1C-ED4E-4391-9E33-AD3B64561A40}" - } - } - } - } - }, { "Id": { "id": 56991021999544 @@ -868,87 +824,233 @@ }, { "Id": { - "id": 8400576252253 + "id": 16962627423626 }, "Name": "SC-Node(AuthorityToClientNoParams_PlayFxByEntityId)", "Components": { - "Component_[6332803108634970671]": { + "Component_[3643560316791874936]": { "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", - "Id": 6332803108634970671, + "Id": 3643560316791874936, "Slots": [ { "id": { - "m_id": "{87B7266B-D7B1-4CAD-9898-4D7F0274DAB0}" + "m_id": "{029728DF-0939-4D64-A9A1-3DB4B8AF127E}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Source", + "toolTip": "The Source containing the NetworkTestLevelEntityComponentController", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 + }, + { + "id": { + "m_id": "{2C322CF8-1A5C-48D4-8CD2-9723E2DD4A4D}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "In", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{51F7773F-90F9-4465-8A0E-627EFC30E696}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Out", + "Descriptor": { + "ConnectionType": 2, + "SlotType": 1 + } + } + ], + "Datums": [ + { + "isOverloadedStorage": false, + "scriptCanvasType": { + "m_type": 1 + }, + "isNullPointer": false, + "$type": "EntityId", + "value": { + "id": 2901262558 + }, + "label": "Source" + } + ], + "methodType": 2, + "methodName": "AuthorityToClientNoParams_PlayFxByEntityId", + "className": "NetworkTestLevelEntityComponent", + "inputSlots": [ + { + "m_id": "{029728DF-0939-4D64-A9A1-3DB4B8AF127E}" + } + ], + "prettyClassName": "NetworkTestLevelEntityComponent" + } + } + }, + { + "Id": { + "id": 12482976533898 + }, + "Name": "SC-EventNode(AuthorityToClientNoParams_PlayFx Notify Event)", + "Components": { + "Component_[3959674633056578794]": { + "$type": "AzEventHandler", + "Id": 3959674633056578794, + "Slots": [ + { + "id": { + "m_id": "{D9C7482D-62F3-494B-ABD8-DEE1EC423F5B}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "ConnectionLimitContract", + "limit": 1 + }, + { + "$type": "RestrictedNodeContract", + "m_nodeId": { + "id": 11993350262154 + } + } + ], + "slotName": "Connect", + "toolTip": "Connect the AZ Event to this AZ Event Handler.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{79E07161-43E8-46B6-A402-ACEF441621ED}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + } + ], + "slotName": "Disconnect", + "toolTip": "Disconnect current AZ Event from this AZ Event Handler.", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 1 + } + }, + { + "id": { + "m_id": "{0DA1B9BC-BAA1-4144-B9A8-C64A3C1EF3B7}" }, "contracts": [ { "$type": "SlotTypeContract" } ], - "slotName": "Source", - "toolTip": "The Source containing the NetworkTestPlayerComponentController", + "slotName": "On Connected", + "toolTip": "Signaled when a connection has taken place.", "Descriptor": { - "ConnectionType": 1, - "SlotType": 2 - }, - "DataType": 1 + "ConnectionType": 2, + "SlotType": 1 + } }, { "id": { - "m_id": "{AB0D7C00-A334-449A-AC56-EA3167AB8900}" + "m_id": "{F978B3BE-22DC-48EA-B004-D9C7FA7B32E8}" }, "contracts": [ { "$type": "SlotTypeContract" } ], - "slotName": "In", + "slotName": "On Disconnected", + "toolTip": "Signaled when this event handler is disconnected.", "Descriptor": { - "ConnectionType": 1, + "ConnectionType": 2, "SlotType": 1 } }, { "id": { - "m_id": "{A52302D6-9DF9-45C2-960D-19BF90A4A931}" + "m_id": "{27F4E9A2-F450-4519-8358-D13D823DA33F}" }, "contracts": [ { "$type": "SlotTypeContract" } ], - "slotName": "Out", + "slotName": "OnEvent", + "toolTip": "Triggered when the AZ Event invokes Signal() function.", "Descriptor": { "ConnectionType": 2, "SlotType": 1 - } + }, + "IsLatent": true + }, + { + "id": { + "m_id": "{3CCA270B-6726-48D0-8523-154B42269D61}" + }, + "contracts": [ + { + "$type": "SlotTypeContract" + }, + { + "$type": "ConnectionLimitContract", + "limit": 1 + }, + { + "$type": "RestrictedNodeContract", + "m_nodeId": { + "id": 11993350262154 + } + } + ], + "slotName": "AuthorityToClientNoParams_PlayFx Notify Event", + "Descriptor": { + "ConnectionType": 1, + "SlotType": 2 + }, + "DataType": 1 } ], "Datums": [ { + "isOverloadedStorage": false, "scriptCanvasType": { - "m_type": 1 - }, - "isNullPointer": false, - "$type": "EntityId", - "value": { - "id": 2901262558 + "m_type": 4, + "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}" }, - "label": "Source" + "isNullPointer": true, + "label": "AuthorityToClientNoParams_PlayFx Notify Event" } ], - "methodType": 2, - "methodName": "AuthorityToClientNoParams_PlayFxByEntityId", - "className": "NetworkTestPlayerComponent", - "resultSlotIDs": [ - {} - ], - "inputSlots": [ - { - "m_id": "{87B7266B-D7B1-4CAD-9898-4D7F0274DAB0}" + "m_azEventEntry": { + "m_eventName": "AuthorityToClientNoParams_PlayFx Notify Event", + "m_eventSlotId": { + "m_id": "{3CCA270B-6726-48D0-8523-154B42269D61}" } - ], - "prettyClassName": "NetworkTestPlayerComponent" + } } } }, @@ -1090,111 +1192,6 @@ } } } - }, - { - "Id": { - "id": 7820402811489 - }, - "Name": "SC-Node(GetAuthorityToClientNoParams_PlayFxEventByEntityId)", - "Components": { - "Component_[9263945554457190064]": { - "$type": "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF} Method", - "Id": 9263945554457190064, - "Slots": [ - { - "id": { - "m_id": "{F22A7438-E72F-4757-90D9-99F03C91E10D}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "EntityId: 0", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 2 - }, - "DataType": 1 - }, - { - "id": { - "m_id": "{510F56FD-6778-4DB8-BDDE-258335431CC6}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "In", - "Descriptor": { - "ConnectionType": 1, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{94C2AF04-6BFA-4E5A-9490-C7479A7AF61E}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "Out", - "Descriptor": { - "ConnectionType": 2, - "SlotType": 1 - } - }, - { - "id": { - "m_id": "{9C6DDF96-6BF0-45ED-B15F-2E6C2FF5F886}" - }, - "contracts": [ - { - "$type": "SlotTypeContract" - } - ], - "slotName": "Event<>", - "DisplayDataType": { - "m_type": 4, - "m_azType": "{F429F985-AF00-529B-8449-16E56694E5F9}" - }, - "Descriptor": { - "ConnectionType": 2, - "SlotType": 2 - }, - "DataType": 1 - } - ], - "Datums": [ - { - "scriptCanvasType": { - "m_type": 1 - }, - "isNullPointer": false, - "$type": "EntityId", - "value": { - "id": 2901262558 - }, - "label": "EntityId: 0" - } - ], - "methodType": 2, - "methodName": "GetAuthorityToClientNoParams_PlayFxEventByEntityId", - "className": "NetworkTestPlayerComponent", - "resultSlotIDs": [ - {} - ], - "inputSlots": [ - { - "m_id": "{F22A7438-E72F-4757-90D9-99F03C91E10D}" - } - ], - "prettyClassName": "NetworkTestPlayerComponent" - } - } } ], "m_connections": [ @@ -1254,34 +1251,6 @@ } } }, - { - "Id": { - "id": 9392713697629 - }, - "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(AuthorityToClientNoParams_PlayFxByEntityId: In)", - "Components": { - "Component_[17811480012084226596]": { - "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 17811480012084226596, - "sourceEndpoint": { - "nodeId": { - "id": 56986727032248 - }, - "slotId": { - "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}" - } - }, - "targetEndpoint": { - "nodeId": { - "id": 8400576252253 - }, - "slotId": { - "m_id": "{AB0D7C00-A334-449A-AC56-EA3167AB8900}" - } - } - } - } - }, { "Id": { "id": 10269167405311 @@ -1368,27 +1337,55 @@ }, { "Id": { - "id": 9022993654369 + "id": 42045766425613 + }, + "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(Print: In)", + "Components": { + "Component_[1911118463107071864]": { + "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", + "Id": 1911118463107071864, + "sourceEndpoint": { + "nodeId": { + "id": 56986727032248 + }, + "slotId": { + "m_id": "{C1CCBA7B-A13B-4FCE-99ED-8FD1A8F72869}" + } + }, + "targetEndpoint": { + "nodeId": { + "id": 56999611934136 + }, + "slotId": { + "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}" + } + } + } + } + }, + { + "Id": { + "id": 13187351170442 }, "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: Event<>), destEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: AuthorityToClientNoParams_PlayFx Notify Event)", "Components": { - "Component_[4910818715692868417]": { + "Component_[16956267859815935178]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 4910818715692868417, + "Id": 16956267859815935178, "sourceEndpoint": { "nodeId": { - "id": 7820402811489 + "id": 11993350262154 }, "slotId": { - "m_id": "{9C6DDF96-6BF0-45ED-B15F-2E6C2FF5F886}" + "m_id": "{F120FEE1-448B-4D61-8439-10511C797707}" } }, "targetEndpoint": { "nodeId": { - "id": 8318619017825 + "id": 12482976533898 }, "slotId": { - "m_id": "{5F809F1C-ED4E-4391-9E33-AD3B64561A40}" + "m_id": "{3CCA270B-6726-48D0-8523-154B42269D61}" } } } @@ -1396,27 +1393,27 @@ }, { "Id": { - "id": 9078828229217 + "id": 13243185745290 }, "Name": "srcEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: Out), destEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: Connect)", "Components": { - "Component_[16758724763058723803]": { + "Component_[8562287491617113016]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 16758724763058723803, + "Id": 8562287491617113016, "sourceEndpoint": { "nodeId": { - "id": 7820402811489 + "id": 11993350262154 }, "slotId": { - "m_id": "{94C2AF04-6BFA-4E5A-9490-C7479A7AF61E}" + "m_id": "{3FD9A3D0-FFAA-475E-89CB-D24EB4FEB952}" } }, "targetEndpoint": { "nodeId": { - "id": 8318619017825 + "id": 12482976533898 }, "slotId": { - "m_id": "{2A42C379-8E3B-46EF-BC63-C1D5395CB583}" + "m_id": "{D9C7482D-62F3-494B-ABD8-DEE1EC423F5B}" } } } @@ -1424,13 +1421,13 @@ }, { "Id": { - "id": 9808972669537 + "id": 14252503059850 }, "Name": "srcEndpoint=(TimeDelay: Done), destEndpoint=(GetAuthorityToClientNoParams_PlayFxEventByEntityId: In)", "Components": { - "Component_[597205010205160938]": { + "Component_[2852756465133807472]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 597205010205160938, + "Id": 2852756465133807472, "sourceEndpoint": { "nodeId": { "id": 57012496836024 @@ -1441,10 +1438,10 @@ }, "targetEndpoint": { "nodeId": { - "id": 7820402811489 + "id": 11993350262154 }, "slotId": { - "m_id": "{510F56FD-6778-4DB8-BDDE-258335431CC6}" + "m_id": "{4D460DFD-82A1-4A92-8EAE-D32A96E79FA0}" } } } @@ -1452,27 +1449,27 @@ }, { "Id": { - "id": 10148275085921 + "id": 14690589724042 }, - "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(Print: In)", + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(DrawTextOnEntity: In)", "Components": { - "Component_[1594149632687531010]": { + "Component_[5236071663388486964]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 1594149632687531010, + "Id": 5236071663388486964, "sourceEndpoint": { "nodeId": { - "id": 8318619017825 + "id": 12482976533898 }, "slotId": { - "m_id": "{0481BBFE-D31E-421F-A6C2-8A7AF3012545}" + "m_id": "{27F4E9A2-F450-4519-8358-D13D823DA33F}" } }, "targetEndpoint": { "nodeId": { - "id": 56995316966840 + "id": 57003906901432 }, "slotId": { - "m_id": "{7CAD6E31-6218-4326-8FFB-0523F545E250}" + "m_id": "{1673B8A0-D4EC-4CC7-8F80-0419BB5560EB}" } } } @@ -1480,27 +1477,27 @@ }, { "Id": { - "id": 10629311423073 + "id": 14943992794506 }, - "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(DrawTextOnEntity: In)", + "Name": "srcEndpoint=(AuthorityToClientNoParams_PlayFx Notify Event: OnEvent), destEndpoint=(Print: In)", "Components": { - "Component_[17976200298405988971]": { + "Component_[3230673372890452861]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 17976200298405988971, + "Id": 3230673372890452861, "sourceEndpoint": { "nodeId": { - "id": 8318619017825 + "id": 12482976533898 }, "slotId": { - "m_id": "{0481BBFE-D31E-421F-A6C2-8A7AF3012545}" + "m_id": "{27F4E9A2-F450-4519-8358-D13D823DA33F}" } }, "targetEndpoint": { "nodeId": { - "id": 57003906901432 + "id": 56995316966840 }, "slotId": { - "m_id": "{1673B8A0-D4EC-4CC7-8F80-0419BB5560EB}" + "m_id": "{7CAD6E31-6218-4326-8FFB-0523F545E250}" } } } @@ -1508,13 +1505,13 @@ }, { "Id": { - "id": 42045766425613 + "id": 18109383691658 }, - "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(Print: In)", + "Name": "srcEndpoint=(Repeater: Action), destEndpoint=(AuthorityToClientNoParams_PlayFxByEntityId: In)", "Components": { - "Component_[1911118463107071864]": { + "Component_[12099207352632363628]": { "$type": "{64CA5016-E803-4AC4-9A36-BDA2C890C6EB} Connection", - "Id": 1911118463107071864, + "Id": 12099207352632363628, "sourceEndpoint": { "nodeId": { "id": 56986727032248 @@ -1525,10 +1522,10 @@ }, "targetEndpoint": { "nodeId": { - "id": 56999611934136 + "id": 16962627423626 }, "slotId": { - "m_id": "{2F11CF04-DC4B-4881-8D74-AB0E51B4A278}" + "m_id": "{2C322CF8-1A5C-48D4-8CD2-9723E2DD4A4D}" } } } @@ -1545,7 +1542,7 @@ "GraphCanvasData": [ { "Key": { - "id": 7820402811489 + "id": 8310662318335 }, "Value": { "ComponentData": { @@ -1559,8 +1556,8 @@ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - -100.0, - 400.0 + 80.0, + -320.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { @@ -1569,14 +1566,14 @@ }, "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { "$type": "PersistentIdComponentSaveData", - "PersistentId": "{F35F8202-B5EE-4ADD-9FF6-AF214A094266}" + "PersistentId": "{346AFC26-B2EF-4495-AA1A-347BF77CB99D}" } } } }, { "Key": { - "id": 8310662318335 + "id": 11993350262154 }, "Value": { "ComponentData": { @@ -1590,8 +1587,8 @@ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 80.0, - -320.0 + -120.0, + 340.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { @@ -1600,14 +1597,14 @@ }, "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { "$type": "PersistentIdComponentSaveData", - "PersistentId": "{346AFC26-B2EF-4495-AA1A-347BF77CB99D}" + "PersistentId": "{AF658C01-E781-416E-B719-70C171E3FA18}" } } } }, { "Key": { - "id": 8318619017825 + "id": 12482976533898 }, "Value": { "ComponentData": { @@ -1622,7 +1619,7 @@ "$type": "GeometrySaveData", "Position": [ 340.0, - 400.0 + 360.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { @@ -1631,14 +1628,14 @@ }, "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { "$type": "PersistentIdComponentSaveData", - "PersistentId": "{67499699-CA73-48B4-87E0-C66F4A3EA7CB}" + "PersistentId": "{404C56EB-23C4-4AC8-A08A-752725B19703}" } } } }, { "Key": { - "id": 8400576252253 + "id": 16962627423626 }, "Value": { "ComponentData": { @@ -1652,8 +1649,8 @@ "{7CC444B1-F9B3-41B5-841B-0C4F2179F111}": { "$type": "GeometrySaveData", "Position": [ - 420.0, - -60.0 + 440.0, + -40.0 ] }, "{B0B99C8A-03AF-4CF6-A926-F65C874C3D97}": { @@ -1662,7 +1659,7 @@ }, "{B1F49A35-8408-40DA-B79E-F1E3B64322CE}": { "$type": "PersistentIdComponentSaveData", - "PersistentId": "{2B6329F7-4CE7-4E01-B1A4-1FFCAB2D0B72}" + "PersistentId": "{E06094C1-8911-4FB6-9D28-E02B808F1DB1}" } } } @@ -1880,15 +1877,16 @@ }, { "Key": { - "id": 1685762441320719908 + "id": 7369225496155711251 }, "Value": { "ComponentData": { "{5F84B500-8C45-40D1-8EFC-A5306B241444}": { "$type": "SceneComponentSaveData", "ViewParams": { - "AnchorX": -349.0, - "AnchorY": 10.0 + "Scale": 0.7585823890144868, + "AnchorX": -205.64674377441406, + "AnchorY": -467.9781799316406 } } } @@ -1898,23 +1896,23 @@ "StatisticsHelper": { "InstanceCounter": [ { - "Key": 4199610336680704683, + "Key": 435784057388502002, "Value": 1 }, { - "Key": 4847610523576971761, + "Key": 4199610336680704683, "Value": 1 }, { - "Key": 6462358712820489356, + "Key": 4847610523576971761, "Value": 1 }, { - "Key": 7087687843968394353, + "Key": 5317247366618270757, "Value": 1 }, { - "Key": 8679770052035517025, + "Key": 6462358712820489356, "Value": 1 }, { diff --git a/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Android.cpp b/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Android.cpp index ea65cf3e59..c51bfaf04e 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Android.cpp +++ b/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Android.cpp @@ -209,12 +209,10 @@ namespace ProjectSettingsTool ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PackageName)) ->Attribute(Attributes::LinkOptional, true) ->Attribute(Attributes::PropertyIdentfier, Identfiers::AndroidPackageName) - ->Attribute(Attributes::LinkedProperty, Identfiers::IosBundleIdentifer) ->DataElement(Handlers::LinkedLineEdit, &AndroidSettings::m_versionName, "Version Name", "Human readable version number. Used to set the \"android: versionName\" tag in the AndroidManifest.xml and ultimately what will be displayed in the App Store.") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IOSVersionNumber)) ->Attribute(Attributes::LinkOptional, true) ->Attribute(Attributes::PropertyIdentfier, Identfiers::AndroidVersionName) - ->Attribute(Attributes::LinkedProperty, Identfiers::IosVersionName) ->DataElement(AZ::Edit::UIHandlers::Default, &AndroidSettings::m_versionNumber, "Version Number", "Internal application version number. Used to set the \"android:versionCode\" tag in the AndroidManifest.xml.") ->Attribute(AZ::Edit::Attributes::Min, 1) ->Attribute(AZ::Edit::Attributes::Max, Validators::maxAndroidVersion) diff --git a/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Base.cpp b/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Base.cpp index eff524b13e..d011289272 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Base.cpp +++ b/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Base.cpp @@ -37,19 +37,15 @@ namespace ProjectSettingsTool ->DataElement(Handlers::LinkedLineEdit, &BaseSettings::m_projectName, "Project Name", "The name of the project.") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileName)) ->Attribute(Attributes::PropertyIdentfier, Identfiers::ProjectName) - ->Attribute(Attributes::LinkedProperty, Identfiers::IosBundleName) ->DataElement(Handlers::LinkedLineEdit, &BaseSettings::m_productName, "Product Name", "The project's user facing name.") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IsNotEmpty)) ->Attribute(Attributes::PropertyIdentfier, Identfiers::ProductName) - ->Attribute(Attributes::LinkedProperty, Identfiers::IosDisplayName) ->DataElement(Handlers::LinkedLineEdit, &BaseSettings::m_executableName, "Executable Name", "The project launcher's name.") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileName)) ->Attribute(Attributes::PropertyIdentfier, Identfiers::ExecutableName) - ->Attribute(Attributes::LinkedProperty, Identfiers::IosExecutableName) ->DataElement(Handlers::QValidatedLineEdit, &BaseSettings::m_projectPath, "Project Path", "The project root folder path .") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileNameOrEmpty)) ->Attribute(Attributes::PropertyIdentfier, Identfiers::ProductName) - ->Attribute(Attributes::LinkedProperty, Identfiers::ExecutableName) ->DataElement(Handlers::QValidatedLineEdit, &BaseSettings::m_projectOutputFolder, "Output Folder", "The folder the packed project will be exported to.") ->DataElement(Handlers::QValidatedLineEdit, &BaseSettings::m_codeFolder, "Code Folder (legacy)", "A legacy setting specifing the folder for this project's code.") ; diff --git a/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Ios.cpp b/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Ios.cpp index fd6aef019e..739a5c94da 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Ios.cpp +++ b/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_Ios.cpp @@ -262,27 +262,22 @@ namespace ProjectSettingsTool ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_bundleName, "Bundle Name", "The name of the bundle.") - ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileName)) + ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IOSFileName)) ->Attribute(Attributes::PropertyIdentfier, Identfiers::IosBundleName) - ->Attribute(Attributes::LinkedProperty, Identfiers::ProjectName) ->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_bundleDisplayName, "Display Name", "The user visible name of the bundle.") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IsNotEmpty)) ->Attribute(Attributes::PropertyIdentfier, Identfiers::IosDisplayName) - ->Attribute(Attributes::LinkedProperty, Identfiers::ProductName) ->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_executableName, "Executable Name", "Name of the bundle's executable file.") - ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::FileName)) + ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IOSFileName)) ->Attribute(Attributes::PropertyIdentfier, Identfiers::IosExecutableName) - ->Attribute(Attributes::LinkedProperty, Identfiers::ExecutableName) ->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_bundleIdentifier, "Bundle Identifier", "Uniquely identifies the bundle. Should be in reverse-DNS format.") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::PackageName)) ->Attribute(Attributes::LinkOptional, true) ->Attribute(Attributes::PropertyIdentfier, Identfiers::IosBundleIdentifer) - ->Attribute(Attributes::LinkedProperty, Identfiers::AndroidPackageName) ->DataElement(Handlers::LinkedLineEdit, &IosSettings::m_versionName, "Version Name", "The release version number string for the app. Displayed in the app store.") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IOSVersionNumber)) ->Attribute(Attributes::LinkOptional, true) ->Attribute(Attributes::PropertyIdentfier, Identfiers::IosVersionName) - ->Attribute(Attributes::LinkedProperty, Identfiers::AndroidVersionName) ->DataElement(Handlers::QValidatedLineEdit, &IosSettings::m_versionNumber, "Version Number", "The build version number string for the bundle.") ->Attribute(Attributes::FuncValidator, ConvertFunctorToVoid(&Validators::IOSVersionNumber)) ->DataElement(AZ::Edit::UIHandlers::ComboBox, &IosSettings::m_developmentRegion, "Development Region", "The default language and region for the app.") diff --git a/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_common.h b/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_common.h index ab15b2d3cd..ffc9625b53 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_common.h +++ b/Code/Editor/Plugins/ProjectSettingsTool/PlatformSettings_common.h @@ -22,7 +22,6 @@ namespace ProjectSettingsTool static const AZ::Crc32 Obfuscated = AZ_CRC("ObfuscatedText"); // Used as a tooltip and for distinguising linked properties static const AZ::Crc32 PropertyIdentfier = AZ_CRC("PropertyIdentfier"); - static const AZ::Crc32 LinkedProperty = AZ_CRC("LinkedProperty"); static const AZ::Crc32 DefaultPath = AZ_CRC("DefaultPath"); static const AZ::Crc32 DefaultImagePreview = AZ_CRC("DefaultImagePreview"); static const AZ::Crc32 ObfuscatedText = AZ_CRC("ObfuscatedText"); diff --git a/Code/Editor/Plugins/ProjectSettingsTool/PropertyLinked.cpp b/Code/Editor/Plugins/ProjectSettingsTool/PropertyLinked.cpp index b48155f1f8..118c11b739 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/PropertyLinked.cpp +++ b/Code/Editor/Plugins/ProjectSettingsTool/PropertyLinked.cpp @@ -225,25 +225,6 @@ namespace ProjectSettingsTool } } } - else if (attrib == Attributes::LinkedProperty) - { - AZStd::string linked; - if (attrValue->Read(linked)) - { - auto result = m_ctrlToIdentAndLink.find(GUI); - if (result != m_ctrlToIdentAndLink.end()) - { - result->second.linkedIdentifier = linked; - } - else - { - m_ctrlToIdentAndLink.insert(AZStd::pair(GUI, IdentAndLink{ "", linked })); - m_ctrlInitOrder.push_back(GUI); - } - - GUI->SetLinkTooltip(linked.data()); - } - } else { GUI->ConsumeAttribute(attrib, attrValue, debugName); diff --git a/Code/Editor/Plugins/ProjectSettingsTool/Validators.cpp b/Code/Editor/Plugins/ProjectSettingsTool/Validators.cpp index 4dfc4ee25a..5bf3f5ce67 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/Validators.cpp +++ b/Code/Editor/Plugins/ProjectSettingsTool/Validators.cpp @@ -106,6 +106,11 @@ namespace ProjectSettingsTool return RegularExpressionValidator("[\\w,-]+", name); } + // Returns true if valid iOS file or directory name + RetType IOSFileName(const QString& name) + { + return RegularExpressionValidator("[\\w,-.]+", name); + } RetType FileNameOrEmpty(const QString& name) { if (IsNotEmpty(name).first == QValidator::Acceptable) diff --git a/Code/Editor/Plugins/ProjectSettingsTool/Validators.h b/Code/Editor/Plugins/ProjectSettingsTool/Validators.h index bd0abb19d6..7cf3498eef 100644 --- a/Code/Editor/Plugins/ProjectSettingsTool/Validators.h +++ b/Code/Editor/Plugins/ProjectSettingsTool/Validators.h @@ -24,6 +24,8 @@ namespace ProjectSettingsTool // Returns true if valid cross platform file or directory name FunctorValidator::ReturnType FileName(const QString& name); + // Returns true if valid iOS file or directory name + FunctorValidator::ReturnType IOSFileName(const QString& name); // Returns true if valid cross platform file or directory name or empty FunctorValidator::ReturnType FileNameOrEmpty(const QString& name); // Returns true if string isn't empty diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp index 196ce28216..f7e41c0c00 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.cpp @@ -203,7 +203,7 @@ namespace AZ JsonSerializationResult::Result JsonMapSerializer::LoadElement(void* outputValue, SerializeContext::IDataContainer* container, const SerializeContext::ClassElement* pairElement, SerializeContext::IDataContainer* pairContainer, const SerializeContext::ClassElement* keyElement, const SerializeContext::ClassElement* valueElement, - const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context) + const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context, bool isMultiMap) { namespace JSR = JsonSerializationResult; @@ -231,8 +231,30 @@ namespace AZ return context.Report(keyResult, "Failed to read key for associative container."); } + void* valueAddress = nullptr; + bool keyExists = false; + + // For multimaps, we append values to keys instead updating them. + // This is to ensure legacy multimap serialization support. + if (!isMultiMap) + { + auto associativeContainer = container->GetAssociativeContainerInterface(); + void* existingKeyValuePair = associativeContainer->GetElementByKey(outputValue, keyElement, keyAddress); + if (existingKeyValuePair) + { + valueAddress = pairContainer->GetElementByIndex(existingKeyValuePair, pairElement, 1); + expectedSize--; + keyExists = true; + } + } + + // If the key doesn't exist or it's a multimap, we're adding the new element we reserved above. + if (!keyExists) + { + valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1); + } + // Load value - void* valueAddress = pairContainer->GetElementByIndex(address, pairElement, 1); AZ_Assert(valueAddress, "Element reserved for associative container, but unable to retrieve address of the value."); ContinuationFlags valueLoadFlags = ContinuationFlags::LoadAsNewInstance; if (valueElement->m_flags & SerializeContext::ClassElement::Flags::FLG_POINTER) @@ -257,7 +279,18 @@ namespace AZ } else { - container->StoreElement(outputValue, address); + // Even if the key exists, calling StoreElement will not replace the existing key + // and will free the temporary address as expected. Checking if the key already + // exists and skipping the call to StoreElement if it does, makes the intent more + // clear. The end result is the same either way. + if (!keyExists) + { + container->StoreElement(outputValue, address); + } + else + { + container->FreeReservedElement(outputValue, address, context.GetSerializeContext()); + } if (container->Size(outputValue) != expectedSize) { return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unavailable, @@ -430,7 +463,7 @@ namespace AZ JsonSerializationResult::Result JsonUnorderedMultiMapSerializer::LoadElement(void* outputValue, SerializeContext::IDataContainer* container, const SerializeContext::ClassElement* pairElement, SerializeContext::IDataContainer* pairContainer, const SerializeContext::ClassElement* keyElement, const SerializeContext::ClassElement* valueElement, - const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context) + const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context, [[maybe_unused]] bool isMultiMap) { namespace JSR = JsonSerializationResult; @@ -440,7 +473,7 @@ namespace AZ for (auto& entry : value.GetArray()) { result.Combine(JsonMapSerializer::LoadElement(outputValue, container, pairElement, pairContainer, - keyElement, valueElement, key, entry, context)); + keyElement, valueElement, key, entry, context, true)); if (result.GetProcessing() == JSR::Processing::Halted) { return context.Report(result, "Unable to process the key or all values in multi-map."); @@ -451,7 +484,7 @@ namespace AZ else if (IsExplicitDefault(value)) { return JsonMapSerializer::LoadElement(outputValue, container, pairElement, pairContainer, - keyElement, valueElement, key, value, context); + keyElement, valueElement, key, value, context, true); } else { diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.h b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.h index 937c8389ff..74d7e81ac9 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/MapSerializer.h @@ -32,7 +32,7 @@ namespace AZ virtual JsonSerializationResult::Result LoadElement(void* outputValue, SerializeContext::IDataContainer* container, const SerializeContext::ClassElement* pairElement, SerializeContext::IDataContainer* pairContainer, const SerializeContext::ClassElement* keyElement, const SerializeContext::ClassElement* valueElement, - const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context); + const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context, bool isMultiMap = false); virtual JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, const Uuid& valueTypeId, JsonSerializerContext& context, bool sortResult); @@ -62,7 +62,7 @@ namespace AZ JsonSerializationResult::Result LoadElement(void* outputValue, SerializeContext::IDataContainer* container, const SerializeContext::ClassElement* pairElement, SerializeContext::IDataContainer* pairContainer, const SerializeContext::ClassElement* keyElement, const SerializeContext::ClassElement* valueElement, - const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context) override; + const rapidjson::Value& key, const rapidjson::Value& value, JsonDeserializerContext& context, bool isMultiMap = false) override; using JsonMapSerializer::Store; JsonSerializationResult::Result Store(rapidjson::Value& outputValue, const void* inputValue, const void* defaultValue, diff --git a/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp b/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp index 7ffe3ffee0..fe97d36f08 100644 --- a/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp +++ b/Code/Framework/AzCore/Tests/Serialization/Json/MapSerializerTests.cpp @@ -475,7 +475,7 @@ namespace JsonSerializationTests EXPECT_STRCASEEQ("value_42", worldKey->second.m_value.c_str()); } - TEST_F(JsonMapSerializerTests, Load_DuplicateKey_EntryIgnored) + TEST_F(JsonMapSerializerTests, Load_DuplicateKey_EntryUpdated) { using namespace AZ::JsonSerializationResult; @@ -489,12 +489,12 @@ namespace JsonSerializationTests StringMap values; ResultCode result = m_unorderedMapSerializer.Load(&values, azrtti_typeid(&values), *m_jsonDocument, *m_jsonDeserializationContext); - EXPECT_EQ(Processing::PartialAlter, result.GetProcessing()); - EXPECT_EQ(Outcomes::Unavailable, result.GetOutcome()); + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_EQ(Outcomes::Success, result.GetOutcome()); auto entry = values.find("Hello"); ASSERT_NE(values.end(), entry); - EXPECT_STRCASEEQ("World", entry->second.c_str()); + EXPECT_EQ("Other", entry->second); } TEST_F(JsonMapSerializerTests, Load_DuplicateMultiKey_LoadEverything) @@ -536,8 +536,8 @@ namespace JsonSerializationTests ResultCode result = m_unorderedMapSerializer.Load(&values, azrtti_typeid(&values), *m_jsonDocument, *m_jsonDeserializationContext); - EXPECT_EQ(Processing::Altered, result.GetProcessing()); - EXPECT_EQ(Outcomes::Unavailable, result.GetOutcome()); + EXPECT_EQ(Processing::Completed, result.GetProcessing()); + EXPECT_EQ(Outcomes::Success, result.GetOutcome()); auto entry = values.find("Hello"); ASSERT_NE(values.end(), entry); diff --git a/Code/Legacy/CryCommon/WinBase.cpp b/Code/Legacy/CryCommon/WinBase.cpp index 771cde324e..6e6f5e210a 100644 --- a/Code/Legacy/CryCommon/WinBase.cpp +++ b/Code/Legacy/CryCommon/WinBase.cpp @@ -856,18 +856,4 @@ DLL_EXPORT void OutputDebugString(const char* outputString) #endif -// This code does not have a long life span and will be replaced soon -#if defined(APPLE) || defined(LINUX) || defined(DEFINE_LEGACY_CRY_FILE_OPERATIONS) - -bool CrySetFileAttributes(const char* lpFileName, uint32 dwFileAttributes) -{ - //TODO: implement - printf("CrySetFileAttributes not properly implemented yet\n"); - return false; -} - - - -#endif //defined(APPLE) || defined(LINUX) - #endif // AZ_TRAIT_LEGACY_CRYCOMMON_USE_WINDOWS_STUBS diff --git a/Code/Legacy/CryCommon/platform.h b/Code/Legacy/CryCommon/platform.h index d2251c7091..512f8b4892 100644 --- a/Code/Legacy/CryCommon/platform.h +++ b/Code/Legacy/CryCommon/platform.h @@ -336,7 +336,6 @@ void SetFlags(T& dest, U flags, bool b) #include AZ_RESTRICTED_FILE(platform_h) #endif -bool CrySetFileAttributes(const char* lpFileName, uint32 dwFileAttributes); threadID CryGetCurrentThreadId(); #ifdef __GNUC__ diff --git a/Code/Legacy/CryCommon/platform_impl.cpp b/Code/Legacy/CryCommon/platform_impl.cpp index 8cbc58ad95..3392c40771 100644 --- a/Code/Legacy/CryCommon/platform_impl.cpp +++ b/Code/Legacy/CryCommon/platform_impl.cpp @@ -24,7 +24,6 @@ #define PLATFORM_IMPL_H_SECTION_TRAITS 1 #define PLATFORM_IMPL_H_SECTION_CRYLOWLATENCYSLEEP 2 #define PLATFORM_IMPL_H_SECTION_CRYGETFILEATTRIBUTES 3 -#define PLATFORM_IMPL_H_SECTION_CRYSETFILEATTRIBUTES 4 #define PLATFORM_IMPL_H_SECTION_CRY_FILE_ATTRIBUTE_STUBS 5 #define PLATFORM_IMPL_H_SECTION_CRY_SYSTEM_FUNCTIONS 6 #define PLATFORM_IMPL_H_SECTION_VIRTUAL_ALLOCATORS 7 @@ -238,22 +237,6 @@ void InitRootDir(char szExeFileName[], uint nExeSize, char szExeRootName[], uint } } -////////////////////////////////////////////////////////////////////////// -bool CrySetFileAttributes(const char* lpFileName, uint32 dwFileAttributes) -{ -#if defined(AZ_RESTRICTED_PLATFORM) - #define AZ_RESTRICTED_SECTION PLATFORM_IMPL_H_SECTION_CRYSETFILEATTRIBUTES - #include AZ_RESTRICTED_FILE(platform_impl_h) -#endif -#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED) -#undef AZ_RESTRICTED_SECTION_IMPLEMENTED -#else - AZStd::wstring lpFileNameW; - AZStd::to_wstring(lpFileNameW, lpFileName); - return SetFileAttributes(lpFileNameW.c_str(), dwFileAttributes) != 0; -#endif -} - ////////////////////////////////////////////////////////////////////////// threadID CryGetCurrentThreadId() { diff --git a/Code/Legacy/CrySystem/XML/xml.cpp b/Code/Legacy/CrySystem/XML/xml.cpp index fb0714500c..2356e71518 100644 --- a/Code/Legacy/CrySystem/XML/xml.cpp +++ b/Code/Legacy/CrySystem/XML/xml.cpp @@ -1132,7 +1132,10 @@ bool CXmlNode::saveToFile(const char* fileName) bool CXmlNode::saveToFile([[maybe_unused]] const char* fileName, size_t chunkSize, AZ::IO::HandleType fileHandle) { - CrySetFileAttributes(fileName, FILE_ATTRIBUTE_NORMAL); + if (AZ::IO::SystemFile::Exists(fileName) && !AZ::IO::SystemFile::IsWritable(fileName)) + { + AZ::IO::SystemFile::SetWritable(fileName, true); + } if (chunkSize < 256 * 1024) // make at least 256k { diff --git a/Code/Tools/ProjectManager/Resources/ProjectManager.qss b/Code/Tools/ProjectManager/Resources/ProjectManager.qss index 3d100ec170..b2217435b2 100644 --- a/Code/Tools/ProjectManager/Resources/ProjectManager.qss +++ b/Code/Tools/ProjectManager/Resources/ProjectManager.qss @@ -500,6 +500,10 @@ QProgressBar::chunk { /************** Gem Catalog **************/ +#GemCatalogScreen { + background-color: #333333; +} + #GemCatalogTitle { font-size: 18px; } @@ -546,9 +550,8 @@ QProgressBar::chunk { min-height:24px; } -#GemCatalogHeaderLabel { - font-size: 12px; - color: #FFFFFF; +#adjustableHeaderWidget QHeaderView::section { + background-color: transparent; } #GemCatalogHeaderShowCountLabel { @@ -732,15 +735,6 @@ QProgressBar::chunk { stop: 0 #555555, stop: 1.0 #777777); } -#gemRepoHeaderTable { - background-color: transparent; - max-height: 30px; -} - -#gemRepoListHeader { - background-color: transparent; -} - #gemRepoInspector { background: #444444; } @@ -774,4 +768,4 @@ QProgressBar::chunk { #gemRepoInspectorAddInfoTitleLabel { font-size: 16px; color: #FFFFFF; -} \ No newline at end of file +} diff --git a/Code/Tools/ProjectManager/Source/AdjustableHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/AdjustableHeaderWidget.cpp new file mode 100644 index 0000000000..2b4732a168 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/AdjustableHeaderWidget.cpp @@ -0,0 +1,118 @@ +/* + * 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 +#include + +#include +#include + +namespace O3DE::ProjectManager +{ + AdjustableHeaderWidget::AdjustableHeaderWidget( const QStringList& headerLabels, + const QVector& defaultHeaderWidths, int minHeaderWidth, + const QVector& resizeModes, QWidget* parent) + : QTableWidget(parent) + { + setObjectName("adjustableHeaderWidget"); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + setFixedHeight(s_headerWidgetHeight); + + m_header = horizontalHeader(); + m_header->setDefaultAlignment(Qt::AlignLeft); + + setColumnCount(headerLabels.count()); + setHorizontalHeaderLabels(headerLabels); + + AZ_Assert(defaultHeaderWidths.count() == columnCount(), "Default header widths does not match number of columns"); + AZ_Assert(resizeModes.count() == columnCount(), "Resize modesdoes not match number of columns"); + + for (int column = 0; column < columnCount(); ++column) + { + m_header->resizeSection(column, defaultHeaderWidths[column]); + m_header->setSectionResizeMode(column, resizeModes[column]); + } + + m_header->setMinimumSectionSize(minHeaderWidth); + m_header->setCascadingSectionResizes(true); + + connect(m_header, &QHeaderView::sectionResized, this, &AdjustableHeaderWidget::OnSectionResized); + } + + void AdjustableHeaderWidget::OnSectionResized(int logicalIndex, int oldSize, int newSize) + { + const int headerCount = columnCount(); + const int headerWidth = m_header->width(); + const int totalSectionWidth = m_header->length(); + + if (totalSectionWidth > headerWidth && newSize > oldSize) + { + int xPos = 0; + int requiredWidth = 0; + + for (int i = 0; i < headerCount; i++) + { + if (i < logicalIndex) + { + xPos += m_header->sectionSize(i); + } + else if (i == logicalIndex) + { + xPos += newSize; + } + else if (i > logicalIndex) + { + if (m_header->sectionResizeMode(i) == QHeaderView::ResizeMode::Fixed) + { + requiredWidth += m_header->sectionSize(i); + } + else + { + requiredWidth += m_header->minimumSectionSize(); + } + } + } + + if (xPos + requiredWidth > headerWidth) + { + m_header->resizeSection(logicalIndex, oldSize); + } + } + + // wait till all columns resized + QTimer::singleShot(0, [&]() + { + // only re-paint when the header and section widths have settled + const int headerWidth = m_header->width(); + const int totalSectionWidth = m_header->length(); + if (totalSectionWidth == headerWidth) + { + emit sectionsResized(); + } + }); + } + + QPair AdjustableHeaderWidget::CalcColumnXBounds(int headerIndex) const + { + // Total the widths of all headers before this one in first and including it in second + QPair bounds(0, 0); + + for (int curIndex = 0; curIndex <= headerIndex; ++curIndex) + { + if (curIndex == headerIndex) + { + bounds.first = bounds.second; + } + bounds.second += m_header->sectionSize(curIndex); + } + + return bounds; + } + + +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/AdjustableHeaderWidget.h b/Code/Tools/ProjectManager/Source/AdjustableHeaderWidget.h new file mode 100644 index 0000000000..26a5ce13c7 --- /dev/null +++ b/Code/Tools/ProjectManager/Source/AdjustableHeaderWidget.h @@ -0,0 +1,52 @@ +/* + * 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 + +#if !defined(Q_MOC_RUN) +#include + +#include +#include +#include +#include +#endif + +namespace O3DE::ProjectManager +{ + // Using a QTableWidget for its header + // Using a seperate model allows the setup of a header exactly as needed + class AdjustableHeaderWidget + : public QTableWidget + { + Q_OBJECT + + public: + explicit AdjustableHeaderWidget(const QStringList& headerLabels, + const QVector& defaultHeaderWidths, int minHeaderWidth, + const QVector& resizeModes, + QWidget* parent = nullptr); + ~AdjustableHeaderWidget() = default; + + QPair CalcColumnXBounds(int headerIndex) const; + + inline constexpr static int s_headerTextIndent = 7; + inline constexpr static int s_headerWidgetHeight = 24; + + QHeaderView* m_header; + + signals: + void sectionsResized(); + + protected slots: + void OnSectionResized(int logicalIndex, int oldSize, int newSize); + + private: + inline constexpr static int s_headerIndentSection = 11; + }; +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ExternalLinkDialog.h b/Code/Tools/ProjectManager/Source/ExternalLinkDialog.h index 45d391e64e..f1b6574b67 100644 --- a/Code/Tools/ProjectManager/Source/ExternalLinkDialog.h +++ b/Code/Tools/ProjectManager/Source/ExternalLinkDialog.h @@ -17,7 +17,7 @@ namespace O3DE::ProjectManager class ExternalLinkDialog : public QDialog { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit ExternalLinkDialog(const QUrl& url, QWidget* parent = nullptr); ~ExternalLinkDialog() = default; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h index b749e9831d..4e945f5c99 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogHeaderWidget.h @@ -33,7 +33,7 @@ namespace O3DE::ProjectManager class GemCartWidget : public QScrollArea { - Q_OBJECT // AUTOMOC + Q_OBJECT public: GemCartWidget(GemModel* gemModel, DownloadController* downloadController, QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp index 5bc0b26ca5..ebabff45cc 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemCatalogScreen.cpp @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -40,6 +42,13 @@ namespace O3DE::ProjectManager GemCatalogScreen::GemCatalogScreen(QWidget* parent) : ScreenWidget(parent) { + // The width of either side panel (filters, inspector) in the catalog + constexpr int sidePanelWidth = 240; + // Querying qApp about styling reports the scroll bar being larger than it is so define it manually + constexpr int verticalScrollBarWidth = 8; + + setObjectName("GemCatalogScreen"); + m_gemModel = new GemModel(this); m_proxyModel = new GemSortFilterProxyModel(m_gemModel, this); @@ -69,10 +78,8 @@ namespace O3DE::ProjectManager hLayout->setMargin(0); vLayout->addLayout(hLayout); - m_gemListView = new GemListView(m_proxyModel, m_proxyModel->GetSelectionModel(), this); - m_rightPanelStack = new QStackedWidget(this); - m_rightPanelStack->setFixedWidth(240); + m_rightPanelStack->setFixedWidth(sidePanelWidth); m_gemInspector = new GemInspector(m_gemModel, this); @@ -81,18 +88,45 @@ namespace O3DE::ProjectManager connect(m_gemInspector, &GemInspector::UninstallGem, this, &GemCatalogScreen::UninstallGem); QWidget* filterWidget = new QWidget(this); - filterWidget->setFixedWidth(240); + filterWidget->setFixedWidth(sidePanelWidth); m_filterWidgetLayout = new QVBoxLayout(); m_filterWidgetLayout->setMargin(0); m_filterWidgetLayout->setSpacing(0); filterWidget->setLayout(m_filterWidgetLayout); - GemListHeaderWidget* listHeaderWidget = new GemListHeaderWidget(m_proxyModel); + GemListHeaderWidget* catalogHeaderWidget = new GemListHeaderWidget(m_proxyModel); + + constexpr int minHeaderSectionWidth = 100; + AdjustableHeaderWidget* listHeaderWidget = new AdjustableHeaderWidget( + QStringList{ tr("Gem Name"), tr("Gem Summary"), tr("Status") }, + QVector{ + GemItemDelegate::s_defaultSummaryStartX - 30, + 0, // Section is set to stretch to fit + GemItemDelegate::s_buttonWidth + GemItemDelegate::s_itemMargins.left() + GemItemDelegate::s_itemMargins.right() + GemItemDelegate::s_contentMargins.right() + }, + minHeaderSectionWidth, + QVector + { + QHeaderView::ResizeMode::Interactive, + QHeaderView::ResizeMode::Stretch, + QHeaderView::ResizeMode::Fixed + }, + this); + + m_gemListView = new GemListView(m_proxyModel, m_proxyModel->GetSelectionModel(), listHeaderWidget, this); + + QHBoxLayout* listHeaderLayout = new QHBoxLayout(); + listHeaderLayout->setMargin(0); + listHeaderLayout->setSpacing(0); + listHeaderLayout->addSpacing(GemItemDelegate::s_itemMargins.left()); + listHeaderLayout->addWidget(listHeaderWidget); + listHeaderLayout->addSpacing(GemItemDelegate::s_itemMargins.right() + verticalScrollBarWidth); QVBoxLayout* middleVLayout = new QVBoxLayout(); middleVLayout->setMargin(0); middleVLayout->setSpacing(0); - middleVLayout->addWidget(listHeaderWidget); + middleVLayout->addWidget(catalogHeaderWidget); + middleVLayout->addLayout(listHeaderLayout); middleVLayout->addWidget(m_gemListView); hLayout->addWidget(filterWidget); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.h index df8ca6f8a2..1858637aa5 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemDependenciesDialog.h @@ -19,7 +19,7 @@ namespace O3DE::ProjectManager class GemDependenciesDialog : public QDialog { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemDependenciesDialog(GemModel* gemModel, QWidget *parent = nullptr); ~GemDependenciesDialog() = default; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h index e422178d08..729a63af64 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemFilterWidget.h @@ -26,7 +26,7 @@ namespace O3DE::ProjectManager class FilterCategoryWidget : public QWidget { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit FilterCategoryWidget(const QString& header, diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h index 1713191623..c71d43eaac 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInspector.h @@ -28,7 +28,7 @@ namespace O3DE::ProjectManager class GemInspector : public QScrollArea { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemInspector(GemModel* model, QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp index dd94e42fc4..1733257e3b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.cpp @@ -9,6 +9,8 @@ #include #include #include +#include + #include #include @@ -22,12 +24,14 @@ #include #include #include +#include namespace O3DE::ProjectManager { - GemItemDelegate::GemItemDelegate(QAbstractItemModel* model, QObject* parent) + GemItemDelegate::GemItemDelegate(QAbstractItemModel* model, AdjustableHeaderWidget* header, QObject* parent) : QStyledItemDelegate(parent) , m_model(model) + , m_headerWidget(header) { AddPlatformIcon(GemInfo::Android, ":/Android.svg"); AddPlatformIcon(GemInfo::iOS, ":/iOS.svg"); @@ -116,12 +120,15 @@ namespace O3DE::ProjectManager // Gem name QString gemName = GemModel::GetDisplayName(modelIndex); QFont gemNameFont(options.font); - const int firstColumnMaxTextWidth = s_summaryStartX - 30; + QPair nameXBounds = CalcColumnXBounds(HeaderOrder::Name); + const int nameStartX = nameXBounds.first; + const int firstColumnTextStartX = s_itemMargins.left() + nameStartX + AdjustableHeaderWidget::s_headerTextIndent; + const int firstColumnMaxTextWidth = nameXBounds.second - nameStartX - AdjustableHeaderWidget::s_headerTextIndent; gemNameFont.setPixelSize(static_cast(s_gemNameFontSize)); gemNameFont.setBold(true); gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); QRect gemNameRect = GetTextRect(gemNameFont, gemName, s_gemNameFontSize); - gemNameRect.moveTo(contentRect.left(), contentRect.top()); + gemNameRect.moveTo(firstColumnTextStartX, contentRect.top()); painter->setFont(gemNameFont); painter->setPen(m_textColor); gemNameRect = painter->boundingRect(gemNameRect, Qt::TextSingleLine, gemName); @@ -131,7 +138,7 @@ namespace O3DE::ProjectManager QString gemCreator = GemModel::GetCreator(modelIndex); gemCreator = standardFontMetrics.elidedText(gemCreator, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); QRect gemCreatorRect = GetTextRect(standardFont, gemCreator, s_fontSize); - gemCreatorRect.moveTo(contentRect.left(), contentRect.top() + gemNameRect.height()); + gemCreatorRect.moveTo(firstColumnTextStartX, contentRect.top() + gemNameRect.height()); painter->setFont(standardFont); gemCreatorRect = painter->boundingRect(gemCreatorRect, Qt::TextSingleLine, gemCreator); @@ -157,10 +164,13 @@ namespace O3DE::ProjectManager const int featureTagAreaHeight = 30; const int summaryHeight = contentRect.height() - (hasTags * featureTagAreaHeight); - const int additionalSummarySpacing = s_itemMargins.right() * 3; - const QSize summarySize = QSize(contentRect.width() - s_summaryStartX - s_buttonWidth - additionalSummarySpacing, + const auto [summaryStartX, summaryEndX] = CalcColumnXBounds(HeaderOrder::Summary); + + const QSize summarySize = + QSize(summaryEndX - summaryStartX - AdjustableHeaderWidget::s_headerTextIndent - s_extraSummarySpacing, summaryHeight); - return QRect(QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), summarySize); + return QRect( + QPoint(s_itemMargins.left() + summaryStartX + AdjustableHeaderWidget::s_headerTextIndent, contentRect.top()), summarySize); } QSize GemItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const @@ -169,7 +179,7 @@ namespace O3DE::ProjectManager initStyleOption(&options, modelIndex); int marginsHorizontal = s_itemMargins.left() + s_itemMargins.right() + s_contentMargins.left() + s_contentMargins.right(); - return QSize(marginsHorizontal + s_buttonWidth + s_summaryStartX, s_height); + return QSize(marginsHorizontal + s_buttonWidth + s_defaultSummaryStartX, s_height); } bool GemItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) @@ -299,9 +309,17 @@ namespace O3DE::ProjectManager return QFontMetrics(font).boundingRect(text); } + QPair GemItemDelegate::CalcColumnXBounds(HeaderOrder header) const + { + return m_headerWidget->CalcColumnXBounds(static_cast(header)); + } + QRect GemItemDelegate::CalcButtonRect(const QRect& contentRect) const { - const QPoint topLeft = QPoint(contentRect.right() - s_buttonWidth, contentRect.center().y() - s_buttonHeight / 2); + const QPoint topLeft = QPoint( + s_itemMargins.left() + CalcColumnXBounds(HeaderOrder::Status).first + AdjustableHeaderWidget::s_headerTextIndent + s_statusIconSize + + s_statusButtonSpacing, + contentRect.center().y() - s_buttonHeight / 2); const QSize size = QSize(s_buttonWidth, s_buttonHeight); return QRect(topLeft, size); } @@ -331,18 +349,23 @@ namespace O3DE::ProjectManager } } - void GemItemDelegate::DrawFeatureTags(QPainter* painter, const QRect& contentRect, const QStringList& featureTags, const QFont& standardFont, const QRect& summaryRect) const + void GemItemDelegate::DrawFeatureTags( + QPainter* painter, + const QRect& contentRect, + const QStringList& featureTags, + const QFont& standardFont, + const QRect& summaryRect) const { QFont gemFeatureTagFont(standardFont); gemFeatureTagFont.setPixelSize(s_featureTagFontSize); gemFeatureTagFont.setBold(false); painter->setFont(gemFeatureTagFont); - int x = s_summaryStartX; + int x = CalcColumnXBounds(HeaderOrder::Summary).first + AdjustableHeaderWidget::s_headerTextIndent; for (const QString& featureTag : featureTags) { QRect featureTagRect = GetTextRect(gemFeatureTagFont, featureTag, s_featureTagFontSize); - featureTagRect.moveTo(contentRect.left() + x + s_featureTagBorderMarginX, + featureTagRect.moveTo(s_itemMargins.left() + x + s_featureTagBorderMarginX, contentRect.top() + 47); featureTagRect = painter->boundingRect(featureTagRect, Qt::TextSingleLine, featureTag); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h index 107de6de15..a08fcb0a4b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemItemDelegate.h @@ -19,13 +19,15 @@ QT_FORWARD_DECLARE_CLASS(QEvent) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(AdjustableHeaderWidget) + class GemItemDelegate : public QStyledItemDelegate { - Q_OBJECT // AUTOMOC + Q_OBJECT public: - explicit GemItemDelegate(QAbstractItemModel* model, QObject* parent = nullptr); + explicit GemItemDelegate(QAbstractItemModel* model, AdjustableHeaderWidget* header, QObject* parent = nullptr); ~GemItemDelegate() = default; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; @@ -45,12 +47,13 @@ namespace O3DE::ProjectManager inline constexpr static int s_height = 105; // Gem item total height inline constexpr static qreal s_gemNameFontSize = 13.0; inline constexpr static qreal s_fontSize = 12.0; - inline constexpr static int s_summaryStartX = 150; + inline constexpr static int s_defaultSummaryStartX = 190; // Margin and borders inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/16, /*top=*/8, /*right=*/16, /*bottom=*/8); // Item border distances inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/12, /*right=*/20, /*bottom=*/12); // Distances of the elements within an item to the item borders inline constexpr static int s_borderWidth = 4; + inline constexpr static int s_extraSummarySpacing = s_itemMargins.right(); // Button inline constexpr static int s_buttonWidth = 32; @@ -65,6 +68,13 @@ namespace O3DE::ProjectManager inline constexpr static int s_featureTagBorderMarginY = 3; inline constexpr static int s_featureTagSpacing = 7; + enum class HeaderOrder + { + Name, + Summary, + Status + }; + signals: void MovieStartedPlaying(const QMovie* playingMovie) const; @@ -74,13 +84,20 @@ namespace O3DE::ProjectManager void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const; + QPair CalcColumnXBounds(HeaderOrder header) const; QRect CalcButtonRect(const QRect& contentRect) const; QRect CalcSummaryRect(const QRect& contentRect, bool hasTags) const; void DrawPlatformIcons(QPainter* painter, const QRect& contentRect, const QModelIndex& modelIndex) const; void DrawButton(QPainter* painter, const QRect& buttonRect, const QModelIndex& modelIndex) const; - void DrawFeatureTags(QPainter* painter, const QRect& contentRect, const QStringList& featureTags, const QFont& standardFont, const QRect& summaryRect) const; + void DrawFeatureTags( + QPainter* painter, + const QRect& contentRect, + const QStringList& featureTags, + const QFont& standardFont, + const QRect& summaryRect) const; void DrawText(const QString& text, QPainter* painter, const QRect& rect, const QFont& standardFont) const; - void DrawDownloadStatusIcon(QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const; + void DrawDownloadStatusIcon( + QPainter* painter, const QRect& contentRect, const QRect& buttonRect, const QModelIndex& modelIndex) const; QAbstractItemModel* m_model = nullptr; @@ -100,5 +117,7 @@ namespace O3DE::ProjectManager QPixmap m_downloadSuccessfulPixmap; QPixmap m_downloadFailedPixmap; QMovie* m_downloadingMovie = nullptr; + + AdjustableHeaderWidget* m_headerWidget = nullptr; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp index 10ff31f33b..ec54b413b6 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.cpp @@ -78,37 +78,9 @@ namespace O3DE::ProjectManager // Separating line QFrame* hLine = new QFrame(); hLine->setFrameShape(QFrame::HLine); - hLine->setStyleSheet("color: #666666;"); + hLine->setObjectName("horizontalSeparatingLine"); vLayout->addWidget(hLine); vLayout->addSpacing(GemItemDelegate::s_contentMargins.top()); - - // Bottom section - QHBoxLayout* columnHeaderLayout = new QHBoxLayout(); - columnHeaderLayout->setAlignment(Qt::AlignLeft); - - const int gemNameStartX = GemItemDelegate::s_itemMargins.left() + GemItemDelegate::s_contentMargins.left() - 1; - columnHeaderLayout->addSpacing(gemNameStartX); - - QLabel* gemNameLabel = new QLabel(tr("Gem Name")); - gemNameLabel->setObjectName("GemCatalogHeaderLabel"); - columnHeaderLayout->addWidget(gemNameLabel); - - columnHeaderLayout->addSpacing(89); - - QLabel* gemSummaryLabel = new QLabel(tr("Gem Summary")); - gemSummaryLabel->setObjectName("GemCatalogHeaderLabel"); - columnHeaderLayout->addWidget(gemSummaryLabel); - - QSpacerItem* horizontalSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); - columnHeaderLayout->addSpacerItem(horizontalSpacer); - - QLabel* gemSelectedLabel = new QLabel(tr("Status")); - gemSelectedLabel->setObjectName("GemCatalogHeaderLabel"); - columnHeaderLayout->addWidget(gemSelectedLabel); - - columnHeaderLayout->addSpacing(72); - - vLayout->addLayout(columnHeaderLayout); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h index 537748c849..350c17bbf9 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListHeaderWidget.h @@ -19,7 +19,7 @@ namespace O3DE::ProjectManager class GemListHeaderWidget : public QFrame { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemListHeaderWidget(GemSortFilterProxyModel* proxyModel, QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp index cfdf7fa5b3..d68cbf511b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.cpp @@ -8,12 +8,15 @@ #include #include +#include #include +#include namespace O3DE::ProjectManager { - GemListView::GemListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent) + GemListView::GemListView( + QAbstractItemModel* model, QItemSelectionModel* selectionModel, AdjustableHeaderWidget* header, QWidget* parent) : QListView(parent) { setObjectName("GemCatalogListView"); @@ -21,7 +24,7 @@ namespace O3DE::ProjectManager setModel(model); setSelectionModel(selectionModel); - GemItemDelegate* itemDelegate = new GemItemDelegate(model, this); + GemItemDelegate* itemDelegate = new GemItemDelegate(model, header, this); connect(itemDelegate, &GemItemDelegate::MovieStartedPlaying, [=](const QMovie* playingMovie) { @@ -31,6 +34,8 @@ namespace O3DE::ProjectManager this->viewport()->repaint(); }); }); + + connect(header, &AdjustableHeaderWidget::sectionsResized, [=] { update(); }); setItemDelegate(itemDelegate); } diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.h index b1b0e0c077..81f5255d9b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemListView.h @@ -16,13 +16,15 @@ namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(AdjustableHeaderWidget) + class GemListView : public QListView { - Q_OBJECT // AUTOMOC + Q_OBJECT public: - explicit GemListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent = nullptr); + explicit GemListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, AdjustableHeaderWidget* header, QWidget* parent = nullptr); ~GemListView() = default; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h index cb99581468..50f406c97d 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemModel.h @@ -21,7 +21,7 @@ namespace O3DE::ProjectManager class GemModel : public QStandardItemModel { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemModel(QObject* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.cpp b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.cpp index f4a7148d46..ae4bad8901 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.cpp @@ -16,7 +16,7 @@ namespace O3DE::ProjectManager { GemRequirementDelegate::GemRequirementDelegate(QAbstractItemModel* model, QObject* parent) - : GemItemDelegate(model, parent) + : GemItemDelegate(model, nullptr, parent) { } @@ -54,7 +54,7 @@ namespace O3DE::ProjectManager // Gem name QString gemName = GemModel::GetDisplayName(modelIndex); QFont gemNameFont(options.font); - const int firstColumnMaxTextWidth = s_summaryStartX - 30; + const int firstColumnMaxTextWidth = s_defaultSummaryStartX - 30; gemName = QFontMetrics(gemNameFont).elidedText(gemName, Qt::TextElideMode::ElideRight, firstColumnMaxTextWidth); gemNameFont.setPixelSize(static_cast(s_gemNameFontSize)); gemNameFont.setBold(true); @@ -75,8 +75,8 @@ namespace O3DE::ProjectManager QRect GemRequirementDelegate::CalcRequirementRect(const QRect& contentRect) const { - const QSize requirementSize = QSize(contentRect.width() - s_summaryStartX - s_itemMargins.right(), contentRect.height()); - return QRect(QPoint(contentRect.left() + s_summaryStartX, contentRect.top()), requirementSize); + const QSize requirementSize = QSize(contentRect.width() - s_defaultSummaryStartX - s_itemMargins.right(), contentRect.height()); + return QRect(QPoint(contentRect.left() + s_defaultSummaryStartX, contentRect.top()), requirementSize); } bool GemRequirementDelegate::editorEvent( diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.h index e9001df7fa..cbfb6b1838 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDelegate.h @@ -18,7 +18,7 @@ namespace O3DE::ProjectManager class GemRequirementDelegate : public GemItemDelegate { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemRequirementDelegate(QAbstractItemModel* model, QObject* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.h index af8b1e2cc9..c1dff70ca2 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementDialog.h @@ -19,7 +19,7 @@ namespace O3DE::ProjectManager class GemRequirementDialog : public QDialog { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemRequirementDialog(GemModel* model, QWidget *parent = nullptr); ~GemRequirementDialog() = default; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.h index 7df75f7d94..c527df2ec1 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementFilterProxyModel.h @@ -22,7 +22,7 @@ namespace O3DE::ProjectManager class GemRequirementFilterProxyModel : public QSortFilterProxyModel { - Q_OBJECT // AUTOMOC + Q_OBJECT public: GemRequirementFilterProxyModel(GemModel* sourceModel, QObject* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementListView.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementListView.h index 1638fe3fc5..61b3356e06 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementListView.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemRequirementListView.h @@ -19,7 +19,7 @@ namespace O3DE::ProjectManager class GemRequirementListView : public QListView { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemRequirementListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h index 0c58d66ccf..6bdeaf828b 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemSortFilterProxyModel.h @@ -22,7 +22,7 @@ namespace O3DE::ProjectManager class GemSortFilterProxyModel : public QSortFilterProxyModel { - Q_OBJECT // AUTOMOC + Q_OBJECT public: enum class GemSelected diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemUninstallDialog.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemUninstallDialog.h index 9e3f4c3f3b..391d247f90 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemUninstallDialog.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemUninstallDialog.h @@ -17,7 +17,7 @@ namespace O3DE::ProjectManager class GemUninstallDialog : public QDialog { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemUninstallDialog(const QString& gemName, QWidget *parent = nullptr); ~GemUninstallDialog() = default; diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemUpdateDialog.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemUpdateDialog.h index cf34abfb3d..a0996216fd 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemUpdateDialog.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemUpdateDialog.h @@ -17,7 +17,7 @@ namespace O3DE::ProjectManager class GemUpdateDialog : public QDialog { - Q_OBJECT // AUTOMOC + Q_OBJECT public : explicit GemUpdateDialog(const QString& gemName, bool updateAvaliable = true, QWidget* parent = nullptr); ~GemUpdateDialog() = default; diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h index a14472e6a6..f7051d1704 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoInspector.h @@ -26,7 +26,7 @@ namespace O3DE::ProjectManager { class GemRepoInspector : public QScrollArea { - Q_OBJECT // AUTOMOC + Q_OBJECT public : explicit GemRepoInspector(GemRepoModel* model, QWidget* parent = nullptr); ~GemRepoInspector() = default; diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp index fdfcf02155..672cd509d3 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.cpp @@ -9,16 +9,19 @@ #include #include #include +#include #include #include #include +#include namespace O3DE::ProjectManager { - GemRepoItemDelegate::GemRepoItemDelegate(QAbstractItemModel* model, QObject* parent) + GemRepoItemDelegate::GemRepoItemDelegate(QAbstractItemModel* model, AdjustableHeaderWidget* header, QObject* parent) : QStyledItemDelegate(parent) , m_model(model) + , m_headerWidget(header) { m_refreshIcon = QIcon(":/Refresh.svg").pixmap(s_refreshIconSize, s_refreshIconSize); m_editIcon = QIcon(":/Edit.svg").pixmap(s_iconSize, s_iconSize); @@ -69,44 +72,55 @@ namespace O3DE::ProjectManager painter->restore(); } + int currentHorizontalOffset = CalcColumnXBounds(HeaderOrder::Name).first; + // Repo name QString repoName = GemRepoModel::GetName(modelIndex); - repoName = QFontMetrics(standardFont).elidedText(repoName, Qt::TextElideMode::ElideRight, s_nameMaxWidth); + int sectionSize = m_headerWidget->m_header->sectionSize(static_cast(HeaderOrder::Name)); + repoName = standardFontMetrics.elidedText(repoName, Qt::TextElideMode::ElideRight, + sectionSize - AdjustableHeaderWidget::s_headerTextIndent); QRect repoNameRect = GetTextRect(standardFont, repoName, s_fontSize); - int currentHorizontalOffset = contentRect.left(); - repoNameRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoNameRect.height() / 2); + repoNameRect.moveTo(currentHorizontalOffset + AdjustableHeaderWidget::s_headerTextIndent, + contentRect.center().y() - repoNameRect.height() / 2); repoNameRect = painter->boundingRect(repoNameRect, Qt::TextSingleLine, repoName); painter->drawText(repoNameRect, Qt::TextSingleLine, repoName); // Rem repo creator + currentHorizontalOffset += sectionSize; + sectionSize = m_headerWidget->m_header->sectionSize(static_cast(HeaderOrder::Creator)); + QString repoCreator = GemRepoModel::GetCreator(modelIndex); - repoCreator = standardFontMetrics.elidedText(repoCreator, Qt::TextElideMode::ElideRight, s_creatorMaxWidth); + repoCreator = standardFontMetrics.elidedText(repoCreator, Qt::TextElideMode::ElideRight, + sectionSize - AdjustableHeaderWidget::s_headerTextIndent); QRect repoCreatorRect = GetTextRect(standardFont, repoCreator, s_fontSize); - currentHorizontalOffset += s_nameMaxWidth + s_contentSpacing; - repoCreatorRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoCreatorRect.height() / 2); + repoCreatorRect.moveTo(currentHorizontalOffset + AdjustableHeaderWidget::s_headerTextIndent, + contentRect.center().y() - repoCreatorRect.height() / 2); repoCreatorRect = painter->boundingRect(repoCreatorRect, Qt::TextSingleLine, repoCreator); painter->drawText(repoCreatorRect, Qt::TextSingleLine, repoCreator); // Repo update + currentHorizontalOffset += sectionSize; + sectionSize = m_headerWidget->m_header->sectionSize(static_cast(HeaderOrder::Update)); + QString repoUpdatedDate = GemRepoModel::GetLastUpdated(modelIndex).toString(RepoTimeFormat); - repoUpdatedDate = standardFontMetrics.elidedText(repoUpdatedDate, Qt::TextElideMode::ElideRight, s_updatedMaxWidth); + repoUpdatedDate = standardFontMetrics.elidedText( + repoUpdatedDate, Qt::TextElideMode::ElideRight, + sectionSize - GemRepoItemDelegate::s_refreshIconSpacing - GemRepoItemDelegate::s_refreshIconSize - AdjustableHeaderWidget::s_headerTextIndent); QRect repoUpdatedDateRect = GetTextRect(standardFont, repoUpdatedDate, s_fontSize); - currentHorizontalOffset += s_creatorMaxWidth + s_contentSpacing; - repoUpdatedDateRect.moveTo(currentHorizontalOffset, contentRect.center().y() - repoUpdatedDateRect.height() / 2); + repoUpdatedDateRect.moveTo(currentHorizontalOffset + AdjustableHeaderWidget::s_headerTextIndent, + contentRect.center().y() - repoUpdatedDateRect.height() / 2); repoUpdatedDateRect = painter->boundingRect(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate); painter->drawText(repoUpdatedDateRect, Qt::TextSingleLine, repoUpdatedDate); // Draw refresh button - painter->drawPixmap( - repoUpdatedDateRect.left() + s_updatedMaxWidth + s_refreshIconSpacing, - contentRect.center().y() - s_refreshIconSize / 3, // Dividing size by 3 centers much better - m_refreshIcon); + const QRect refreshButtonRect = CalcRefreshButtonRect(contentRect); + painter->drawPixmap(refreshButtonRect.topLeft(), m_refreshIcon); if (options.state & QStyle::State_MouseOver) { @@ -121,8 +135,8 @@ namespace O3DE::ProjectManager QStyleOptionViewItem options(option); initStyleOption(&options, modelIndex); - int marginsHorizontal = s_itemMargins.left() + s_itemMargins.right() + s_contentMargins.left() + s_contentMargins.right(); - return QSize(marginsHorizontal + s_nameMaxWidth + s_creatorMaxWidth + s_updatedMaxWidth + s_contentSpacing * 3, s_height); + const int marginsHorizontal = s_itemMargins.left() + s_itemMargins.right() + s_contentMargins.left() + s_contentMargins.right(); + return QSize(marginsHorizontal + s_nameDefaultWidth + s_creatorDefaultWidth + s_updatedDefaultWidth, s_height); } bool GemRepoItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) @@ -185,22 +199,31 @@ namespace O3DE::ProjectManager return QFontMetrics(font).boundingRect(text); } + QPair GemRepoItemDelegate::CalcColumnXBounds(HeaderOrder header) const + { + return m_headerWidget->CalcColumnXBounds(static_cast(header)); + } + QRect GemRepoItemDelegate::CalcDeleteButtonRect(const QRect& contentRect) const { - const QPoint topLeft = QPoint(contentRect.right() - s_iconSize, contentRect.center().y() - s_iconSize / 2); + const int deleteHeaderEndX = CalcColumnXBounds(HeaderOrder::Delete).second; + const QPoint topLeft = QPoint(deleteHeaderEndX - s_iconSize - s_contentMargins.right(), contentRect.center().y() - s_iconSize / 2); return QRect(topLeft, QSize(s_iconSize, s_iconSize)); } QRect GemRepoItemDelegate::CalcRefreshButtonRect(const QRect& contentRect) const { - const int topLeftX = contentRect.left() + s_nameMaxWidth + s_creatorMaxWidth + s_updatedMaxWidth + s_contentSpacing * 2 + s_refreshIconSpacing; - const QPoint topLeft = QPoint(topLeftX, contentRect.center().y() - s_refreshIconSize / 3); + const int headerEndX = CalcColumnXBounds(HeaderOrder::Update).second; + const int leftX = headerEndX - s_refreshIconSize - s_refreshIconSpacing; + // Dividing size by 3 centers much better + const QPoint topLeft = QPoint(leftX, contentRect.center().y() - s_refreshIconSize / 3); return QRect(topLeft, QSize(s_refreshIconSize, s_refreshIconSize)); } void GemRepoItemDelegate::DrawEditButtons(QPainter* painter, const QRect& contentRect) const { - painter->drawPixmap(contentRect.right() - s_iconSize, contentRect.center().y() - s_iconSize / 2, m_deleteIcon); + const QRect deleteButtonRect = CalcDeleteButtonRect(contentRect); + painter->drawPixmap(deleteButtonRect, m_deleteIcon); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h index 69d943001d..f8b53e47be 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoItemDelegate.h @@ -18,13 +18,15 @@ QT_FORWARD_DECLARE_CLASS(QEvent) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(AdjustableHeaderWidget) + class GemRepoItemDelegate : public QStyledItemDelegate { - Q_OBJECT // AUTOMOC + Q_OBJECT public: - explicit GemRepoItemDelegate(QAbstractItemModel* model, QObject* parent = nullptr); + explicit GemRepoItemDelegate(QAbstractItemModel* model, AdjustableHeaderWidget* header, QObject* parent = nullptr); ~GemRepoItemDelegate() = default; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& modelIndex) const override; @@ -42,15 +44,14 @@ namespace O3DE::ProjectManager inline constexpr static qreal s_fontSize = 12.0; // Margin and borders - inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/0, /*top=*/8, /*right=*/60, /*bottom=*/8); // Item border distances + inline constexpr static QMargins s_itemMargins = QMargins(/*left=*/0, /*top=*/8, /*right=*/0, /*bottom=*/8); // Item border distances inline constexpr static QMargins s_contentMargins = QMargins(/*left=*/20, /*top=*/20, /*right=*/20, /*bottom=*/20); // Distances of the elements within an item to the item borders inline constexpr static int s_borderWidth = 4; // Content - inline constexpr static int s_contentSpacing = 5; - inline constexpr static int s_nameMaxWidth = 145; - inline constexpr static int s_creatorMaxWidth = 115; - inline constexpr static int s_updatedMaxWidth = 125; + inline constexpr static int s_nameDefaultWidth = 150; + inline constexpr static int s_creatorDefaultWidth = 120; + inline constexpr static int s_updatedDefaultWidth = 130; // Icon inline constexpr static int s_iconSize = 24; @@ -58,6 +59,14 @@ namespace O3DE::ProjectManager inline constexpr static int s_refreshIconSize = 14; inline constexpr static int s_refreshIconSpacing = 10; + enum class HeaderOrder + { + Name, + Creator, + Update, + Delete + }; + signals: void RemoveRepo(const QModelIndex& modelIndex); void RefreshRepo(const QModelIndex& modelIndex); @@ -65,13 +74,15 @@ namespace O3DE::ProjectManager protected: void CalcRects(const QStyleOptionViewItem& option, QRect& outFullRect, QRect& outItemRect, QRect& outContentRect) const; QRect GetTextRect(QFont& font, const QString& text, qreal fontSize) const; - QRect CalcButtonRect(const QRect& contentRect) const; + QPair CalcColumnXBounds(HeaderOrder header) const; QRect CalcDeleteButtonRect(const QRect& contentRect) const; QRect CalcRefreshButtonRect(const QRect& contentRect) const; void DrawEditButtons(QPainter* painter, const QRect& contentRect) const; QAbstractItemModel* m_model = nullptr; + AdjustableHeaderWidget* m_headerWidget = nullptr; + QPixmap m_refreshIcon; QPixmap m_editIcon; QPixmap m_deleteIcon; diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp index 9adf3e6e3f..cf877fd73a 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.cpp @@ -8,12 +8,15 @@ #include #include +#include #include +#include namespace O3DE::ProjectManager { - GemRepoListView::GemRepoListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent) + GemRepoListView::GemRepoListView( + QAbstractItemModel* model, QItemSelectionModel* selectionModel, AdjustableHeaderWidget* header, QWidget* parent) : QListView(parent) { setObjectName("gemRepoListView"); @@ -22,9 +25,10 @@ namespace O3DE::ProjectManager setModel(model); setSelectionModel(selectionModel); - GemRepoItemDelegate* itemDelegate = new GemRepoItemDelegate(model, this); + GemRepoItemDelegate* itemDelegate = new GemRepoItemDelegate(model, header, this); connect(itemDelegate, &GemRepoItemDelegate::RemoveRepo, this, &GemRepoListView::RemoveRepo); connect(itemDelegate, &GemRepoItemDelegate::RefreshRepo, this, &GemRepoListView::RefreshRepo); + connect(header, &AdjustableHeaderWidget::sectionsResized, [=] { update(); }); setItemDelegate(itemDelegate); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h index 50bcf8daa6..7062997f09 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoListView.h @@ -17,13 +17,19 @@ QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) namespace O3DE::ProjectManager { + QT_FORWARD_DECLARE_CLASS(AdjustableHeaderWidget) + class GemRepoListView : public QListView { - Q_OBJECT // AUTOMOC + Q_OBJECT public: - explicit GemRepoListView(QAbstractItemModel* model, QItemSelectionModel* selectionModel, QWidget* parent = nullptr); + explicit GemRepoListView( + QAbstractItemModel* model, + QItemSelectionModel* selectionModel, + AdjustableHeaderWidget* header, + QWidget* parent = nullptr); ~GemRepoListView() = default; signals: diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h index 68991a0509..d1e3975496 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoModel.h @@ -21,7 +21,7 @@ namespace O3DE::ProjectManager class GemRepoModel : public QStandardItemModel { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit GemRepoModel(QObject* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp index 843538d9da..996d8873c5 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include @@ -248,6 +250,9 @@ namespace O3DE::ProjectManager QFrame* GemRepoScreen::CreateReposContent() { + constexpr int inspectorWidth = 240; + constexpr int middleLayoutIndent = 60; + QFrame* contentFrame = new QFrame(this); QHBoxLayout* hLayout = new QHBoxLayout(); @@ -255,7 +260,7 @@ namespace O3DE::ProjectManager hLayout->setSpacing(0); contentFrame->setLayout(hLayout); - hLayout->addSpacing(60); + hLayout->addSpacing(middleLayoutIndent); QVBoxLayout* middleVLayout = new QVBoxLayout(); middleVLayout->setMargin(0); @@ -287,37 +292,34 @@ namespace O3DE::ProjectManager connect(addRepoButton, &QPushButton::clicked, this, &GemRepoScreen::HandleAddRepoButton); - topMiddleHLayout->addSpacing(30); - middleVLayout->addLayout(topMiddleHLayout); middleVLayout->addSpacing(30); - // Create a QTableWidget just for its header - // Using a seperate model allows the setup of a header exactly as needed - m_gemRepoHeaderTable = new QTableWidget(this); - m_gemRepoHeaderTable->setObjectName("gemRepoHeaderTable"); - m_gemRepoListHeader = m_gemRepoHeaderTable->horizontalHeader(); - m_gemRepoListHeader->setObjectName("gemRepoListHeader"); - m_gemRepoListHeader->setDefaultAlignment(Qt::AlignLeft); - m_gemRepoListHeader->setSectionResizeMode(QHeaderView::ResizeMode::Fixed); - - // Insert columns so the header labels will show up - m_gemRepoHeaderTable->insertColumn(0); - m_gemRepoHeaderTable->insertColumn(1); - m_gemRepoHeaderTable->insertColumn(2); - m_gemRepoHeaderTable->setHorizontalHeaderLabels({ tr("Repository Name"), tr("Creator"), tr("Updated") }); - - const int headerExtraMargin = 18; - m_gemRepoListHeader->resizeSection(0, GemRepoItemDelegate::s_nameMaxWidth + GemRepoItemDelegate::s_contentSpacing + headerExtraMargin); - m_gemRepoListHeader->resizeSection(1, GemRepoItemDelegate::s_creatorMaxWidth + GemRepoItemDelegate::s_contentSpacing); - m_gemRepoListHeader->resizeSection(2, GemRepoItemDelegate::s_updatedMaxWidth + GemRepoItemDelegate::s_contentSpacing); - - // Required to set stylesheet in code as it will not be respected if set in qss - m_gemRepoHeaderTable->horizontalHeader()->setStyleSheet("QHeaderView::section { background-color:transparent; color:white; font-size:12px; border-style:none; }"); + constexpr int minHeaderSectionWidth = 120; + + m_gemRepoHeaderTable = new AdjustableHeaderWidget( + QStringList{ tr("Repository Name"), tr("Creator"), tr("Updated"), "" }, + QVector{ + GemRepoItemDelegate::s_nameDefaultWidth, + GemRepoItemDelegate::s_creatorDefaultWidth, + GemRepoItemDelegate::s_updatedDefaultWidth + GemRepoItemDelegate::s_refreshIconSpacing + GemRepoItemDelegate::s_refreshIconSize, + // Include invisible header for delete button + GemRepoItemDelegate::s_iconSize + GemRepoItemDelegate::s_contentMargins.right() + }, + minHeaderSectionWidth, + QVector + { + QHeaderView::ResizeMode::Interactive, + QHeaderView::ResizeMode::Stretch, + QHeaderView::ResizeMode::Fixed, + QHeaderView::ResizeMode::Fixed + }, + this); + middleVLayout->addWidget(m_gemRepoHeaderTable); - m_gemRepoListView = new GemRepoListView(m_gemRepoModel, m_gemRepoModel->GetSelectionModel(), this); + m_gemRepoListView = new GemRepoListView(m_gemRepoModel, m_gemRepoModel->GetSelectionModel(), m_gemRepoHeaderTable, this); middleVLayout->addWidget(m_gemRepoListView); connect(m_gemRepoListView, &GemRepoListView::RemoveRepo, this, &GemRepoScreen::HandleRemoveRepoButton); @@ -325,8 +327,10 @@ namespace O3DE::ProjectManager hLayout->addLayout(middleVLayout); + hLayout->addSpacing(middleLayoutIndent); + m_gemRepoInspector = new GemRepoInspector(m_gemRepoModel, this); - m_gemRepoInspector->setFixedWidth(240); + m_gemRepoInspector->setFixedWidth(inspectorWidth); hLayout->addWidget(m_gemRepoInspector); return contentFrame; diff --git a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h index eed9a5ec4a..643a9b91fc 100644 --- a/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h +++ b/Code/Tools/ProjectManager/Source/GemRepo/GemRepoScreen.h @@ -24,6 +24,7 @@ namespace O3DE::ProjectManager QT_FORWARD_DECLARE_CLASS(GemRepoInspector) QT_FORWARD_DECLARE_CLASS(GemRepoListView) QT_FORWARD_DECLARE_CLASS(GemRepoModel) + QT_FORWARD_DECLARE_CLASS(AdjustableHeaderWidget) class GemRepoScreen : public ScreenWidget @@ -59,7 +60,7 @@ namespace O3DE::ProjectManager QFrame* m_noRepoContent; QFrame* m_repoContent; - QTableWidget* m_gemRepoHeaderTable = nullptr; + AdjustableHeaderWidget* m_gemRepoHeaderTable = nullptr; QHeaderView* m_gemRepoListHeader = nullptr; GemRepoListView* m_gemRepoListView = nullptr; GemRepoInspector* m_gemRepoInspector = nullptr; diff --git a/Code/Tools/ProjectManager/Source/GemsSubWidget.h b/Code/Tools/ProjectManager/Source/GemsSubWidget.h index 5e670b930a..130e6c4282 100644 --- a/Code/Tools/ProjectManager/Source/GemsSubWidget.h +++ b/Code/Tools/ProjectManager/Source/GemsSubWidget.h @@ -22,7 +22,7 @@ namespace O3DE::ProjectManager class GemsSubWidget : public QWidget { - Q_OBJECT // AUTOMOC + Q_OBJECT public: GemsSubWidget(QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.h b/Code/Tools/ProjectManager/Source/LinkWidget.h index eb0b9bb528..ab95c2ecdf 100644 --- a/Code/Tools/ProjectManager/Source/LinkWidget.h +++ b/Code/Tools/ProjectManager/Source/LinkWidget.h @@ -22,7 +22,7 @@ namespace O3DE::ProjectManager class LinkLabel : public QLabel { - Q_OBJECT // AUTOMOC + Q_OBJECT public: LinkLabel(const QString& text = {}, const QUrl& url = {}, int fontSize = 10, QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h index 358c1f249a..c526f7864d 100644 --- a/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/ProjectButtonWidget.h @@ -31,7 +31,7 @@ namespace O3DE::ProjectManager class LabelButton : public QLabel { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit LabelButton(QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h index d784fcf5fd..f184fd2e17 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerDefs.h @@ -11,6 +11,7 @@ namespace O3DE::ProjectManager { + inline constexpr static int MinWindowWidth = 1200; inline constexpr static int ProjectPreviewImageWidth = 210; inline constexpr static int ProjectPreviewImageHeight = 280; inline constexpr static int ProjectTemplateImageWidth = 92; diff --git a/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h index aca0d21fd3..5c000bf61d 100644 --- a/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h +++ b/Code/Tools/ProjectManager/Source/ScreenHeaderWidget.h @@ -20,7 +20,7 @@ namespace O3DE::ProjectManager class ScreenHeader : public QFrame { - Q_OBJECT // AUTOMOC + Q_OBJECT public: ScreenHeader(QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/TagWidget.h b/Code/Tools/ProjectManager/Source/TagWidget.h index fce6eaf863..df817dc506 100644 --- a/Code/Tools/ProjectManager/Source/TagWidget.h +++ b/Code/Tools/ProjectManager/Source/TagWidget.h @@ -27,7 +27,7 @@ namespace O3DE::ProjectManager class TagWidget : public QLabel { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit TagWidget(const Tag& id, QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h index 6216f0e3de..507219d2f2 100644 --- a/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h +++ b/Code/Tools/ProjectManager/Source/TemplateButtonWidget.h @@ -17,7 +17,7 @@ namespace O3DE::ProjectManager class TemplateButton : public QPushButton { - Q_OBJECT // AUTOMOC + Q_OBJECT public: explicit TemplateButton(const QString& imagePath, const QString& labelText, QWidget* parent = nullptr); diff --git a/Code/Tools/ProjectManager/project_manager_files.cmake b/Code/Tools/ProjectManager/project_manager_files.cmake index 915b1a072f..87dfae85e8 100644 --- a/Code/Tools/ProjectManager/project_manager_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_files.cmake @@ -81,6 +81,8 @@ set(FILES Source/TemplateButtonWidget.cpp Source/ExternalLinkDialog.h Source/ExternalLinkDialog.cpp + Source/AdjustableHeaderWidget.h + Source/AdjustableHeaderWidget.cpp Source/GemCatalog/GemCatalogHeaderWidget.h Source/GemCatalog/GemCatalogHeaderWidget.cpp Source/GemCatalog/GemCatalogScreen.h diff --git a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp index a06defff08..fa473cb8c6 100644 --- a/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/CommonSystemComponent.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp index 1aa79d1945..9462ff381f 100644 --- a/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/Mesh/MeshFeatureProcessor.cpp @@ -11,11 +11,11 @@ #include #include #include -#include #include #include #include #include +#include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp index 77031ca3af..f5aaec4faa 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbe.cpp @@ -7,7 +7,7 @@ */ #include -#include +#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp index 3ff574d977..155a159447 100644 --- a/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp @@ -6,12 +6,13 @@ * */ +#include + #include #include #include #include #include -#include #include #include #include diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h b/Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.h similarity index 100% rename from Gems/Atom/Feature/Common/Code/Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h rename to Gems/Atom/Feature/Common/Code/Source/ReflectionProbe/ReflectionProbeFeatureProcessor.h diff --git a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake index 375dbd9724..1ec6402b3e 100644 --- a/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake +++ b/Gems/Atom/Feature/Common/Code/atom_feature_common_files.cmake @@ -31,7 +31,7 @@ set(FILES Include/Atom/Feature/PostProcessing/PostProcessingConstants.h Include/Atom/Feature/PostProcessing/SMAAFeatureProcessorInterface.h Include/Atom/Feature/PostProcess/PostFxLayerCategoriesConstants.h - Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessor.h + Include/Atom/Feature/ReflectionProbe/ReflectionProbeFeatureProcessorInterface.h Include/Atom/Feature/SkyBox/SkyBoxFogBus.h Include/Atom/Feature/SkyBox/SkyboxConstants.h Include/Atom/Feature/SkyBox/SkyBoxLUT.h @@ -272,7 +272,9 @@ set(FILES Source/RayTracing/RayTracingPass.cpp Source/RayTracing/RayTracingPass.h Source/RayTracing/RayTracingPassData.h + Source/ReflectionProbe/ReflectionProbeFeatureProcessor.h Source/ReflectionProbe/ReflectionProbeFeatureProcessor.cpp + Source/ReflectionProbe/ReflectionProbe.h Source/ReflectionProbe/ReflectionProbe.cpp Source/ReflectionScreenSpace/ReflectionScreenSpaceTracePass.cpp Source/ReflectionScreenSpace/ReflectionScreenSpaceTracePass.h diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index 99b3518173..ad6d81d200 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -312,8 +312,11 @@ namespace AZ const View::UsageFlags viewFlags = worklistData->m_view->GetUsageFlags(); const RHI::DrawListMask drawListMask = worklistData->m_view->GetDrawListMask(); - [[maybe_unused]] uint32_t numDrawPackets = 0; - uint32_t numVisibleCullables = 0; + #ifdef AZ_CULL_DEBUG_ENABLED + // These variable are only used for the gathering of debug information. + uint32_t numDrawPackets = 0; + uint32_t numVisibleCullables = 0; + #endif AZ_Assert(worklist.size() > 0, "Received empty worklist in ProcessWorklist"); @@ -351,8 +354,15 @@ namespace AZ if (TestOcclusionCulling(worklistData, visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) #endif { - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view); - ++numVisibleCullables; + // There are ways to write this without [[maybe_unused]], but they are brittle. + // For example, using #else could cause a bug where the function's parameter + // is changed in #ifdef but not in #else. + [[maybe_unused]] const uint32_t drawPacketCount=AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view); + #ifdef AZ_CULL_DEBUG_ENABLED + ++numVisibleCullables; + numDrawPackets += drawPacketCount; + #endif + c->m_isVisible = true; } } @@ -387,8 +397,15 @@ namespace AZ if (TestOcclusionCulling(worklistData, visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE) #endif { - numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view); - ++numVisibleCullables; + // There are ways to write this without [[maybe_unused]], but they are brittle. + // For example, using #else could cause a bug where the function's parameter + // is changed in #ifdef but not in #else. + [[maybe_unused]] const uint32_t drawPacketCount=AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view); + #ifdef AZ_CULL_DEBUG_ENABLED + ++numVisibleCullables; + numDrawPackets += drawPacketCount; + #endif + c->m_isVisible = true; } } diff --git a/Gems/Blast/Editor/Scripts/blast_chunk_processor.py b/Gems/Blast/Editor/Scripts/blast_chunk_processor.py index 500577716b..91a86f9d22 100644 --- a/Gems/Blast/Editor/Scripts/blast_chunk_processor.py +++ b/Gems/Blast/Editor/Scripts/blast_chunk_processor.py @@ -123,6 +123,10 @@ def update_manifest(scene): meshGroup = sceneManifest.add_mesh_group(meshGroupName) meshGroup['id'] = '{' + str(uuid.uuid5(uuid.NAMESPACE_DNS, sourceFilenameOnly + chunkPath)) + '}' sceneManifest.mesh_group_select_node(meshGroup, chunkPath) + # un-select all other mesh nodes + for otherMeshIndex, otherMeshName in enumerate(meshNameList): + if otherMeshName is not chunkName: + sceneManifest.mesh_group_unselect_node(meshGroup, otherMeshName.get_path()) # combine both scene manifests so the OnPrepareForExport will be called originalManifest = json.loads(scene.manifest.ExportToJson()) diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp index a27139f062..d70915a861 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.cpp @@ -72,8 +72,9 @@ namespace Terrain if (auto serialize = azrtti_cast(context)) { serialize->Class() - ->Version(2)->Field( - "Mappings", &TerrainPhysicsColliderConfig::m_surfaceMaterialMappings) + ->Version(3) + ->Field("DefaultMaterial", &TerrainPhysicsColliderConfig::m_defaultMaterialSelection) + ->Field("Mappings", &TerrainPhysicsColliderConfig::m_surfaceMaterialMappings) ; if (auto edit = serialize->GetEditContext()) @@ -84,6 +85,8 @@ namespace Terrain ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainPhysicsColliderConfig::m_defaultMaterialSelection, + "Default Surface Physics Material", "Select a material to be used by unmapped surfaces by default") ->DataElement( AZ::Edit::UIHandlers::Default, &TerrainPhysicsColliderConfig::m_surfaceMaterialMappings, "Surface to Material Mappings", "Maps surfaces to physics materials") @@ -321,7 +324,7 @@ namespace Terrain } // If this surface isn't mapped, use the default material. - return Physics::MaterialId(); + return m_configuration.m_defaultMaterialSelection.GetMaterialId(); } void TerrainPhysicsColliderComponent::GenerateHeightsAndMaterialsInBounds( @@ -419,7 +422,7 @@ namespace Terrain AZStd::vector materialList; // Ensure the list contains the default material as the first entry. - materialList.emplace_back(Physics::MaterialId()); + materialList.emplace_back(m_configuration.m_defaultMaterialSelection.GetMaterialId()); for (auto& mapping : m_configuration.m_surfaceMaterialMappings) { diff --git a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h index 8a70f282d0..5f79fa9103 100644 --- a/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h +++ b/Gems/Terrain/Code/Source/Components/TerrainPhysicsColliderComponent.h @@ -48,7 +48,7 @@ namespace Terrain AZ_CLASS_ALLOCATOR(TerrainPhysicsColliderConfig, AZ::SystemAllocator, 0); AZ_RTTI(TerrainPhysicsColliderConfig, "{E9EADB8F-C3A5-4B9C-A62D-2DBC86B4CE59}", AZ::ComponentConfig); static void Reflect(AZ::ReflectContext* context); - + Physics::MaterialSelection m_defaultMaterialSelection; AZStd::vector m_surfaceMaterialMappings; }; diff --git a/Gems/Terrain/Code/Tests/TerrainPhysicsColliderTests.cpp b/Gems/Terrain/Code/Tests/TerrainPhysicsColliderTests.cpp index dc43544f05..bc4818e62a 100644 --- a/Gems/Terrain/Code/Tests/TerrainPhysicsColliderTests.cpp +++ b/Gems/Terrain/Code/Tests/TerrainPhysicsColliderTests.cpp @@ -46,10 +46,17 @@ protected: appDesc.m_stackRecordLevels = 20; m_app.Create(appDesc); + + CreateEntity(); + + m_boxComponent = m_entity->CreateComponent(); + m_app.RegisterComponentDescriptor(m_boxComponent->CreateDescriptor()); } void TearDown() override { + m_entity.reset(); + m_app.Destroy(); } @@ -61,12 +68,9 @@ protected: m_entity->Init(); } - void AddTerrainPhysicsColliderAndShapeComponentToEntity() + void AddTerrainPhysicsColliderToEntity(const Terrain::TerrainPhysicsColliderConfig& configuration) { - m_boxComponent = m_entity->CreateComponent(); - m_app.RegisterComponentDescriptor(m_boxComponent->CreateDescriptor()); - - m_colliderComponent = m_entity->CreateComponent(Terrain::TerrainPhysicsColliderConfig()); + m_colliderComponent = m_entity->CreateComponent(configuration); m_app.RegisterComponentDescriptor(m_colliderComponent->CreateDescriptor()); } @@ -113,21 +117,17 @@ protected: TEST_F(TerrainPhysicsColliderComponentTest, ActivateEntityActivateSuccess) { // Check that the entity activates with a collider and the required shape attached. - CreateEntity(); - AddTerrainPhysicsColliderAndShapeComponentToEntity(); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); EXPECT_EQ(m_entity->GetState(), AZ::Entity::State::Active); - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderTransformChangedNotifiesHeightfieldBus) { // Check that the HeightfieldBus is notified when the transform of the entity changes. - CreateEntity(); - - AddTerrainPhysicsColliderAndShapeComponentToEntity(); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); @@ -138,16 +138,12 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderTransformChang LmbrCentral::ShapeComponentNotificationsBus::Event( m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged, LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::TransformChanged); - - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderShapeChangedNotifiesHeightfieldBus) { // Check that the Heightfield bus is notified when the shape component changes. - CreateEntity(); - - AddTerrainPhysicsColliderAndShapeComponentToEntity(); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); @@ -157,16 +153,12 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderShapeChangedNo LmbrCentral::ShapeComponentNotificationsBus::Event( m_entity->GetId(), &LmbrCentral::ShapeComponentNotificationsBus::Events::OnShapeChanged, LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged); - - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsAlignedRowBoundsCorrectly) { // Check that the heightfield grid size is correct when the shape bounds match the grid resolution. - CreateEntity(); - - AddTerrainPhysicsColliderAndShapeComponentToEntity(); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); @@ -188,17 +180,13 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsAligned // With the bounds set at 0-1024 and a resolution of 1.0, the heightfield grid should be 1024x1024. EXPECT_EQ(cols, 1024); EXPECT_EQ(rows, 1024); - - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderExpandsMinBoundsCorrectly) { // Check that the heightfield grid is correctly expanded if the minimum value of the bounds needs expanding // to correctly encompass it. - CreateEntity(); - - AddTerrainPhysicsColliderAndShapeComponentToEntity(); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); @@ -221,17 +209,13 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderExpandsMinBoun // the values returned would be 1023. EXPECT_EQ(cols, 1024); EXPECT_EQ(rows, 1024); - - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderExpandsMaxBoundsCorrectly) { // Check that the heightfield grid is correctly expanded if the maximum value of the bounds needs expanding // to correctly encompass it. - CreateEntity(); - - AddTerrainPhysicsColliderAndShapeComponentToEntity(); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); @@ -254,16 +238,12 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderExpandsMaxBoun // the values returned would be 1023. EXPECT_EQ(cols, 1024); EXPECT_EQ(rows, 1024); - - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderGetHeightsReturnsHeights) { // Check that the TerrainPhysicsCollider returns a heightfield of the expected size. - CreateEntity(); - - AddTerrainPhysicsColliderAndShapeComponentToEntity(); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); @@ -298,16 +278,12 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderGetHeightsRetu EXPECT_EQ(cols, 1024); EXPECT_EQ(rows, 1024); EXPECT_EQ(heights.size(), cols * rows); - - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsRelativeHeightsCorrectly) { // Check that the values stored in the heightfield returned by the TerrainPhysicsCollider are correct. - CreateEntity(); - - AddTerrainPhysicsColliderAndShapeComponentToEntity(); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); @@ -341,18 +317,11 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsRelativ const float expectedHeightValue = 16384.0f; EXPECT_NEAR(heights[0], expectedHeightValue, 0.01f); - - m_entity->Reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsMaterials) { // Check that the TerrainPhysicsCollider returns all the assigned materials. - CreateEntity(); - - m_boxComponent = m_entity->CreateComponent(); - m_app.RegisterComponentDescriptor(m_boxComponent->CreateDescriptor()); - // Create two SurfaceTag/Material mappings and add them to the collider. Terrain::TerrainPhysicsColliderConfig config; @@ -372,8 +341,7 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsMateria mapping2.m_surfaceTag = tag2; config.m_surfaceMaterialMappings.emplace_back(mapping2); - m_colliderComponent = m_entity->CreateComponent(config); - m_app.RegisterComponentDescriptor(m_colliderComponent->CreateDescriptor()); + AddTerrainPhysicsColliderToEntity(config); m_entity->Activate(); @@ -388,20 +356,12 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsMateria EXPECT_EQ(materialList[0], defaultMaterial); EXPECT_EQ(materialList[1], mat1); EXPECT_EQ(materialList[2], mat2); - - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsMaterialsWhenNotMapped) { // Check that the TerrainPhysicsCollider returns a default material when no surfaces are mapped. - CreateEntity(); - - m_boxComponent = m_entity->CreateComponent(); - m_app.RegisterComponentDescriptor(m_boxComponent->CreateDescriptor()); - - m_colliderComponent = m_entity->CreateComponent(); - m_app.RegisterComponentDescriptor(m_colliderComponent->CreateDescriptor()); + AddTerrainPhysicsColliderToEntity(Terrain::TerrainPhysicsColliderConfig()); m_entity->Activate(); @@ -414,18 +374,11 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderReturnsMateria Physics::MaterialId defaultMaterial = Physics::MaterialId(); EXPECT_EQ(materialList[0], defaultMaterial); - - m_entity.reset(); } TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderGetHeightsAndMaterialsReturnsCorrectly) { // Check that the TerrainPhysicsCollider returns a heightfield of the expected size. - CreateEntity(); - - m_boxComponent = m_entity->CreateComponent(); - m_app.RegisterComponentDescriptor(m_boxComponent->CreateDescriptor()); - // Create two SurfaceTag/Material mappings and add them to the collider. Terrain::TerrainPhysicsColliderConfig config; @@ -445,8 +398,7 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderGetHeightsAndM mapping2.m_surfaceTag = tag2; config.m_surfaceMaterialMappings.emplace_back(mapping2); - m_colliderComponent = m_entity->CreateComponent(config); - m_app.RegisterComponentDescriptor(m_colliderComponent->CreateDescriptor()); + AddTerrainPhysicsColliderToEntity(config); m_entity->Activate(); @@ -460,15 +412,10 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderGetHeightsAndM const float mockHeight = 32768.0f; AZ::Vector2 mockHeightResolution = AZ::Vector2(1.0f); - AzFramework::SurfaceData::SurfaceTagWeight return1; - return1.m_surfaceType = tag1; - return1.m_weight = 1.0f; - - AzFramework::SurfaceData::SurfaceTagWeight return2; - return2.m_surfaceType = tag2; - return2.m_weight = 1.0f; + AzFramework::SurfaceData::SurfaceTagWeight tagWeight1(tag1, 1.0f); + AzFramework::SurfaceData::SurfaceTagWeight tagWeight2(tag2, 1.0f); - AzFramework::SurfaceData::SurfaceTagWeightList surfaceTags = { return1, return2 }; + AzFramework::SurfaceData::SurfaceTagWeightList surfaceTags = { tagWeight1, tagWeight2 }; NiceMock terrainListener; ON_CALL(terrainListener, GetTerrainHeightQueryResolution).WillByDefault(Return(mockHeightResolution)); @@ -499,6 +446,148 @@ TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderGetHeightsAndM // Check an entry from the second half of the list EXPECT_EQ(heightsAndMaterials[256 * 128].m_materialIndex, 2); EXPECT_NEAR(heightsAndMaterials[256 * 128].m_height, expectedHeightValue, 0.01f); +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderDefaultMaterialAssignedWhenTagHasNoMapping) +{ + // Create two SurfaceTag/Material mappings and add them to the collider. + Terrain::TerrainPhysicsColliderConfig config; + + const Physics::MaterialId defaultSurfaceMaterial = Physics::MaterialId::Create(); + const Physics::MaterialId mat1 = Physics::MaterialId::Create(); + + const SurfaceData::SurfaceTag tag1 = SurfaceData::SurfaceTag("tag1"); + const SurfaceData::SurfaceTag tag2 = SurfaceData::SurfaceTag("tag2"); + + Terrain::TerrainPhysicsSurfaceMaterialMapping mapping1; + mapping1.m_materialId = mat1; + mapping1.m_surfaceTag = tag1; + config.m_surfaceMaterialMappings.emplace_back(mapping1); + config.m_defaultMaterialSelection.SetMaterialId(defaultSurfaceMaterial); + + // Intentionally don't set the mapping for "tag2". It's expected the default material will substitute. + AddTerrainPhysicsColliderToEntity(config); + + m_entity->Activate(); + + // Validate material list is generated with the default material + { + AZStd::vector materialList; + Physics::HeightfieldProviderRequestsBus::EventResult( + materialList, m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetMaterialList); + + // The materialList should be 2 items long: the default material and mat1. + EXPECT_EQ(materialList.size(), 2); + EXPECT_EQ(materialList[0], defaultSurfaceMaterial); + EXPECT_EQ(materialList[1], mat1); + } + + const AZ::Vector3 boundsMin = AZ::Vector3(0.0f); + const AZ::Vector3 boundsMax = AZ::Vector3(256.0f, 256.0f, 32768.0f); + + NiceMock boxShape(m_entity->GetId()); + const AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(boundsMin, boundsMax); + ON_CALL(boxShape, GetEncompassingAabb).WillByDefault(Return(bounds)); + + const float mockHeight = 32768.0f; + AZ::Vector2 mockHeightResolution = AZ::Vector2(1.0f); + + AzFramework::SurfaceData::SurfaceTagWeight tagWeight1(tag1, 1.0f); + AzFramework::SurfaceData::SurfaceTagWeight tagWeight2(tag2, 1.0f); + + AzFramework::SurfaceData::SurfaceTagWeightList surfaceTags = { tagWeight1, tagWeight2 }; + + NiceMock terrainListener; + ON_CALL(terrainListener, GetTerrainHeightQueryResolution).WillByDefault(Return(mockHeightResolution)); + ON_CALL(terrainListener, ProcessSurfacePointsFromRegion).WillByDefault( + [this, mockHeight, &surfaceTags](const AZ::Aabb& inRegion, const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + [[maybe_unused]] AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter) + { + ProcessRegionLoop(inRegion, stepSize, perPositionCallback, &surfaceTags, mockHeight); + } + ); + + // Validate material indices + { + AZStd::vector heightsAndMaterials; + Physics::HeightfieldProviderRequestsBus::EventResult( + heightsAndMaterials, m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); + + // We set the bounds to 256, so check that the correct number of entries are present. + EXPECT_EQ(heightsAndMaterials.size(), 256 * 256); + + // Check an entry from the first half of the returned list. + EXPECT_EQ(heightsAndMaterials[0].m_materialIndex, 1); + + // Check an entry from the second half of the list. + // This should point to the default material (0) since we don't have a mapping for "tag2" + EXPECT_EQ(heightsAndMaterials[256 * 128].m_materialIndex, 0); + } +} + +TEST_F(TerrainPhysicsColliderComponentTest, TerrainPhysicsColliderDefaultMaterialAssignedWhenNoMappingsExist) +{ + // Create only the default material with no mapping for the tags. It's expected the default material will be assigned to both tags. + Terrain::TerrainPhysicsColliderConfig config; + const Physics::MaterialId defaultSurfaceMaterial = Physics::MaterialId::Create(); + config.m_defaultMaterialSelection.SetMaterialId(defaultSurfaceMaterial); + AddTerrainPhysicsColliderToEntity(config); - m_entity.reset(); + m_entity->Activate(); + + // Validate material list is generated with the default material + { + AZStd::vector materialList; + Physics::HeightfieldProviderRequestsBus::EventResult( + materialList, m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetMaterialList); + + EXPECT_EQ(materialList.size(), 1); + EXPECT_EQ(materialList[0], defaultSurfaceMaterial); + } + + const AZ::Vector3 boundsMin = AZ::Vector3(0.0f); + const AZ::Vector3 boundsMax = AZ::Vector3(256.0f, 256.0f, 32768.0f); + + NiceMock boxShape(m_entity->GetId()); + const AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(boundsMin, boundsMax); + ON_CALL(boxShape, GetEncompassingAabb).WillByDefault(Return(bounds)); + + const float mockHeight = 32768.0f; + AZ::Vector2 mockHeightResolution = AZ::Vector2(1.0f); + + const SurfaceData::SurfaceTag tag1 = SurfaceData::SurfaceTag("tag1"); + AzFramework::SurfaceData::SurfaceTagWeight tagWeight1(tag1, 1.0f); + + const SurfaceData::SurfaceTag tag2 = SurfaceData::SurfaceTag("tag2"); + AzFramework::SurfaceData::SurfaceTagWeight tagWeight2(tag2, 1.0f); + + AzFramework::SurfaceData::SurfaceTagWeightList surfaceTags = { tagWeight1, tagWeight2 }; + + NiceMock terrainListener; + ON_CALL(terrainListener, GetTerrainHeightQueryResolution).WillByDefault(Return(mockHeightResolution)); + ON_CALL(terrainListener, ProcessSurfacePointsFromRegion).WillByDefault( + [this, mockHeight, &surfaceTags](const AZ::Aabb& inRegion, const AZ::Vector2& stepSize, + AzFramework::Terrain::SurfacePointRegionFillCallback perPositionCallback, + [[maybe_unused]] AzFramework::Terrain::TerrainDataRequests::Sampler sampleFilter) + { + ProcessRegionLoop(inRegion, stepSize, perPositionCallback, &surfaceTags, mockHeight); + } + ); + + // Validate material indices + { + AZStd::vector heightsAndMaterials; + Physics::HeightfieldProviderRequestsBus::EventResult( + heightsAndMaterials, m_entity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials); + + // We set the bounds to 256, so check that the correct number of entries are present. + EXPECT_EQ(heightsAndMaterials.size(), 256 * 256); + + // Check an entry from the first half of the returned list. Should be the default material index 0. + EXPECT_EQ(heightsAndMaterials[0].m_materialIndex, 0); + + // Check an entry from the second half of the list. Should be the default material index 0. + EXPECT_EQ(heightsAndMaterials[256 * 128].m_materialIndex, 0); + } } diff --git a/scripts/build/Platform/Android/gradle_windows.cmd b/scripts/build/Platform/Android/gradle_windows.cmd index bb98799444..f2d423282f 100644 --- a/scripts/build/Platform/Android/gradle_windows.cmd +++ b/scripts/build/Platform/Android/gradle_windows.cmd @@ -31,11 +31,19 @@ IF NOT EXIST %OUTPUT_DIRECTORY% ( mkdir %OUTPUT_DIRECTORY% ) -REM Jenkins reports MSB8029 when TMP/TEMP is not defined, define a dummy folder -SET TMP=%cd%/temp -SET TEMP=%cd%/temp -IF NOT EXIST %TMP% ( - mkdir %TMP% +REM Jenkins does not defined TMP +IF "%TMP%"=="" ( + IF "%WORKSPACE%"=="" ( + SET TMP=%APPDATA%\Local\Temp + SET TEMP=%APPDATA%\Local\Temp + ) ELSE ( + SET TMP=%WORKSPACE%\Temp + SET TEMP=%WORKSPACE%\Temp + REM This folder may not be created in the workspace + IF NOT EXIST "!TMP!" ( + MKDIR "!TMP!" + ) + ) ) REM Optionally sign the APK if we are generating an APK