Merge remote-tracking branch 'upstream/stabilization/2110' into nvsickle/FixEntityOrdering
This commit is contained in:
@@ -13,9 +13,32 @@
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzFramework/Render/IntersectorInterface.h>
|
||||
#include <AzToolsFramework/Viewport/ViewportMessages.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
|
||||
#include <AzToolsFramework/ViewportSelection/EditorTransformComponentSelectionRequestBus.h>
|
||||
#include <EditorViewportSettings.h>
|
||||
|
||||
AZ_CVAR(
|
||||
bool,
|
||||
ed_cameraPinDefaultOrbit,
|
||||
true,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Sets whether the default orbit point moves with the camera or not");
|
||||
AZ_CVAR(
|
||||
bool,
|
||||
ed_cameraDefaultOrbitAxesOrtho,
|
||||
true,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Sets whether to draw the default orbit point as orthographic or not");
|
||||
AZ_CVAR(
|
||||
float,
|
||||
ed_cameraDefaultOrbitFadeDuration,
|
||||
0.5f,
|
||||
nullptr,
|
||||
AZ::ConsoleFunctorFlags::Null,
|
||||
"Sets how long the default orbit point should take to appear and disappear");
|
||||
|
||||
namespace SandboxEditor
|
||||
{
|
||||
static AzFramework::TranslateCameraInputChannelIds BuildTranslateCameraInputChannelIds()
|
||||
@@ -174,7 +197,7 @@ namespace SandboxEditor
|
||||
return SandboxEditor::CameraScrollSpeed();
|
||||
};
|
||||
|
||||
const auto pivotFn = []
|
||||
const auto pivotFn = []() -> AZStd::optional<AZ::Vector3>
|
||||
{
|
||||
// use the manipulator transform as the pivot point
|
||||
AZStd::optional<AZ::Transform> entityPivot;
|
||||
@@ -187,8 +210,7 @@ namespace SandboxEditor
|
||||
return entityPivot->GetTranslation();
|
||||
}
|
||||
|
||||
// otherwise just use the identity
|
||||
return AZ::Vector3::CreateZero();
|
||||
return AZStd::nullopt;
|
||||
};
|
||||
|
||||
m_firstPersonFocusCamera =
|
||||
@@ -199,9 +221,26 @@ namespace SandboxEditor
|
||||
m_orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>(SandboxEditor::CameraOrbitChannelId());
|
||||
|
||||
m_orbitCamera->SetPivotFn(
|
||||
[pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction)
|
||||
[this, pivotFn](const AZ::Vector3& position, const AZ::Vector3& direction)
|
||||
{
|
||||
return pivotFn();
|
||||
// return the pivot
|
||||
if (auto pivot = pivotFn())
|
||||
{
|
||||
return pivot.value();
|
||||
}
|
||||
|
||||
// start ticking and drawing (for the default pivot)
|
||||
AZ::TickBus::Handler::BusConnect();
|
||||
AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
|
||||
|
||||
m_defaultOrbiting = true;
|
||||
// calculate the default orbit point
|
||||
if (!ed_cameraPinDefaultOrbit || m_orbitCamera->Beginning())
|
||||
{
|
||||
m_defaultOrbitPoint = position + direction * SandboxEditor::CameraDefaultOrbitDistance();
|
||||
}
|
||||
|
||||
return m_defaultOrbitPoint;
|
||||
});
|
||||
|
||||
m_orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(SandboxEditor::CameraOrbitLookChannelId());
|
||||
@@ -306,4 +345,67 @@ namespace SandboxEditor
|
||||
m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorModularViewportCameraComposer::OnTick(const float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
|
||||
{
|
||||
const float delta = [duration = &ed_cameraDefaultOrbitFadeDuration, deltaTime] {
|
||||
if (*duration == 0.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
return deltaTime / *duration;
|
||||
}();
|
||||
|
||||
if (m_defaultOrbiting)
|
||||
{
|
||||
m_defaultOrbitOpacity = AZStd::min(m_defaultOrbitOpacity + delta, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_defaultOrbitOpacity = AZStd::max(m_defaultOrbitOpacity - delta, 0.0f);
|
||||
if (m_defaultOrbitOpacity == 0.0f)
|
||||
{
|
||||
AZ::TickBus::Handler::BusDisconnect();
|
||||
AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
m_defaultOrbiting = false;
|
||||
}
|
||||
|
||||
static void DrawTransformAxis(
|
||||
AzFramework::DebugDisplayRequests& display,
|
||||
const AzFramework::CameraState& cameraState,
|
||||
const AZ::Vector3& pivot,
|
||||
const float axisLength,
|
||||
const float alpha)
|
||||
{
|
||||
const int prevState = display.GetState();
|
||||
|
||||
display.DepthWriteOff();
|
||||
display.DepthTestOff();
|
||||
display.CullOff();
|
||||
|
||||
const float orthoScale =
|
||||
ed_cameraDefaultOrbitAxesOrtho ? AzToolsFramework::CalculateScreenToWorldMultiplier(pivot, cameraState) : 1.0f;
|
||||
|
||||
display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::Red.GetAsVector3(), alpha));
|
||||
display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisX() * axisLength * orthoScale);
|
||||
display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::LawnGreen.GetAsVector3(), alpha));
|
||||
display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisY() * axisLength * orthoScale);
|
||||
display.SetColor(AZ::Color::CreateFromVector3AndFloat(AZ::Colors::Blue.GetAsVector3(), alpha));
|
||||
display.DrawLine(pivot, pivot + AZ::Vector3::CreateAxisZ() * axisLength * orthoScale);
|
||||
|
||||
display.DepthWriteOn();
|
||||
display.DepthTestOn();
|
||||
display.CullOn();
|
||||
|
||||
display.SetState(prevState);
|
||||
}
|
||||
|
||||
void EditorModularViewportCameraComposer::DisplayViewport(
|
||||
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
|
||||
{
|
||||
DrawTransformAxis(
|
||||
debugDisplay, AzToolsFramework::GetCameraState(viewportInfo.m_viewportId), m_defaultOrbitPoint, 1.0f, m_defaultOrbitOpacity);
|
||||
}
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <AtomToolsFramework/Viewport/ModularViewportCameraController.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzFramework/Viewport/CameraInput.h>
|
||||
#include <AzToolsFramework/API/EditorCameraBus.h>
|
||||
#include <EditorModularViewportCameraComposerBus.h>
|
||||
@@ -20,6 +22,8 @@ namespace SandboxEditor
|
||||
class EditorModularViewportCameraComposer
|
||||
: private EditorModularViewportCameraComposerNotificationBus::Handler
|
||||
, private Camera::EditorCameraNotificationBus::Handler
|
||||
, private AzFramework::ViewportDebugDisplayEventBus::Handler
|
||||
, private AZ::TickBus::Handler
|
||||
{
|
||||
public:
|
||||
SANDBOX_API explicit EditorModularViewportCameraComposer(AzFramework::ViewportId viewportId);
|
||||
@@ -29,6 +33,12 @@ namespace SandboxEditor
|
||||
SANDBOX_API AZStd::shared_ptr<AtomToolsFramework::ModularViewportCameraController> CreateModularViewportCameraController();
|
||||
|
||||
private:
|
||||
// AzFramework::ViewportDebugDisplayEventBus overrides ...
|
||||
void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
|
||||
|
||||
// AZ::TickBus overrides ...
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
|
||||
//! Setup all internal camera inputs.
|
||||
void SetupCameras();
|
||||
|
||||
@@ -52,5 +62,9 @@ namespace SandboxEditor
|
||||
AZStd::shared_ptr<AzFramework::FocusCameraInput> m_orbitFocusCamera;
|
||||
|
||||
AzFramework::ViewportId m_viewportId;
|
||||
|
||||
float m_defaultOrbitOpacity = 0.0f; //!< The default orbit axes opacity (to fade in and out).
|
||||
AZ::Vector3 m_defaultOrbitPoint = AZ::Vector3::CreateZero(); //!< The orbit point to use when no entity is selected.
|
||||
bool m_defaultOrbiting = false; //!< Is the camera default orbiting (orbiting when there's no selected entity).
|
||||
};
|
||||
} // namespace SandboxEditor
|
||||
|
||||
@@ -61,7 +61,7 @@ static AZStd::vector<AZStd::string> GetEditorInputNames()
|
||||
void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serialize)
|
||||
{
|
||||
serialize.Class<CameraMovementSettings>()
|
||||
->Version(3)
|
||||
->Version(4)
|
||||
->Field("TranslateSpeed", &CameraMovementSettings::m_translateSpeed)
|
||||
->Field("RotateSpeed", &CameraMovementSettings::m_rotateSpeed)
|
||||
->Field("BoostMultiplier", &CameraMovementSettings::m_boostMultiplier)
|
||||
@@ -76,9 +76,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
->Field("OrbitYawRotationInverted", &CameraMovementSettings::m_orbitYawRotationInverted)
|
||||
->Field("PanInvertedX", &CameraMovementSettings::m_panInvertedX)
|
||||
->Field("PanInvertedY", &CameraMovementSettings::m_panInvertedY)
|
||||
->Field("DefaultPositionX", &CameraMovementSettings::m_defaultCameraPositionX)
|
||||
->Field("DefaultPositionY", &CameraMovementSettings::m_defaultCameraPositionY)
|
||||
->Field("DefaultPositionZ", &CameraMovementSettings::m_defaultCameraPositionZ);
|
||||
->Field("DefaultPosition", &CameraMovementSettings::m_defaultPosition)
|
||||
->Field("DefaultOrbitDistance", &CameraMovementSettings::m_defaultOrbitDistance);
|
||||
|
||||
serialize.Class<CameraInputSettings>()
|
||||
->Version(2)
|
||||
@@ -159,14 +158,12 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial
|
||||
AZ::Edit::UIHandlers::CheckBox, &CameraMovementSettings::m_captureCursorLook, "Camera Capture Look Cursor",
|
||||
"Should the cursor be captured (hidden) while performing free look")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionX, "Default X Camera Position",
|
||||
"Default X Camera Position when a level is opened")
|
||||
AZ::Edit::UIHandlers::Vector3, &CameraMovementSettings::m_defaultPosition, "Default Camera Position",
|
||||
"Default Camera Position when a level is first opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionY, "Default Y Camera Position",
|
||||
"Default Y Camera Position when a level is opened")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultCameraPositionZ, "Default Z Camera Position",
|
||||
"Default Z Camera Position when a level is opened");
|
||||
AZ::Edit::UIHandlers::SpinBox, &CameraMovementSettings::m_defaultOrbitDistance, "Default Orbit Distance",
|
||||
"The default distance to orbit about when there is no entity selected")
|
||||
->Attribute(AZ::Edit::Attributes::Min, minValue);
|
||||
|
||||
editContext->Class<CameraInputSettings>("Camera Input Settings", "")
|
||||
->DataElement(
|
||||
@@ -283,12 +280,8 @@ void CEditorPreferencesPage_ViewportCamera::OnApply()
|
||||
SandboxEditor::SetCameraOrbitYawRotationInverted(m_cameraMovementSettings.m_orbitYawRotationInverted);
|
||||
SandboxEditor::SetCameraPanInvertedX(m_cameraMovementSettings.m_panInvertedX);
|
||||
SandboxEditor::SetCameraPanInvertedY(m_cameraMovementSettings.m_panInvertedY);
|
||||
SandboxEditor::SetDefaultCameraEditorPosition(
|
||||
AZ::Vector3(
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY,
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ
|
||||
));
|
||||
SandboxEditor::SetCameraDefaultEditorPosition(m_cameraMovementSettings.m_defaultPosition);
|
||||
SandboxEditor::SetCameraDefaultOrbitDistance(m_cameraMovementSettings.m_defaultOrbitDistance);
|
||||
|
||||
SandboxEditor::SetCameraTranslateForwardChannelId(m_cameraInputSettings.m_translateForwardChannelId);
|
||||
SandboxEditor::SetCameraTranslateBackwardChannelId(m_cameraInputSettings.m_translateBackwardChannelId);
|
||||
@@ -325,11 +318,8 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings()
|
||||
m_cameraMovementSettings.m_orbitYawRotationInverted = SandboxEditor::CameraOrbitYawRotationInverted();
|
||||
m_cameraMovementSettings.m_panInvertedX = SandboxEditor::CameraPanInvertedX();
|
||||
m_cameraMovementSettings.m_panInvertedY = SandboxEditor::CameraPanInvertedY();
|
||||
|
||||
AZ::Vector3 defaultCameraPosition = SandboxEditor::DefaultEditorCameraPosition();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionX = defaultCameraPosition.GetX();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionY = defaultCameraPosition.GetY();
|
||||
m_cameraMovementSettings.m_defaultCameraPositionZ = defaultCameraPosition.GetZ();
|
||||
m_cameraMovementSettings.m_defaultPosition = SandboxEditor::CameraDefaultEditorPosition();
|
||||
m_cameraMovementSettings.m_defaultOrbitDistance = SandboxEditor::CameraDefaultOrbitDistance();
|
||||
|
||||
m_cameraInputSettings.m_translateForwardChannelId = SandboxEditor::CameraTranslateForwardChannelId().GetName();
|
||||
m_cameraInputSettings.m_translateBackwardChannelId = SandboxEditor::CameraTranslateBackwardChannelId().GetName();
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Include/IPreferencesPage.h"
|
||||
|
||||
#include <AzCore/Math/Vector3.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
inline AZ::Crc32 EditorPropertyVisibility(const bool enabled)
|
||||
@@ -43,6 +46,7 @@ private:
|
||||
{
|
||||
AZ_TYPE_INFO(CameraMovementSettings, "{60B8C07E-5F48-4171-A50B-F45558B5CCA1}")
|
||||
|
||||
AZ::Vector3 m_defaultPosition;
|
||||
float m_translateSpeed;
|
||||
float m_rotateSpeed;
|
||||
float m_scrollSpeed;
|
||||
@@ -50,16 +54,14 @@ private:
|
||||
float m_panSpeed;
|
||||
float m_boostMultiplier;
|
||||
float m_rotateSmoothness;
|
||||
bool m_rotateSmoothing;
|
||||
float m_translateSmoothness;
|
||||
bool m_translateSmoothing;
|
||||
float m_defaultOrbitDistance;
|
||||
bool m_captureCursorLook;
|
||||
bool m_orbitYawRotationInverted;
|
||||
bool m_panInvertedX;
|
||||
bool m_panInvertedY;
|
||||
float m_defaultCameraPositionX;
|
||||
float m_defaultCameraPositionY;
|
||||
float m_defaultCameraPositionZ;
|
||||
bool m_rotateSmoothing;
|
||||
bool m_translateSmoothing;
|
||||
|
||||
AZ::Crc32 RotateSmoothingVisibility() const
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace SandboxEditor
|
||||
constexpr AZStd::string_view CameraTranslateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/TranslateSmoothing";
|
||||
constexpr AZStd::string_view CameraRotateSmoothingSetting = "/Amazon/Preferences/Editor/Camera/RotateSmoothing";
|
||||
constexpr AZStd::string_view CameraCaptureCursorLookSetting = "/Amazon/Preferences/Editor/Camera/CaptureCursorLook";
|
||||
constexpr AZStd::string_view CameraDefaultOrbitDistanceSetting = "/Amazon/Preferences/Editor/Camera/DefaultOrbitDistance";
|
||||
constexpr AZStd::string_view CameraTranslateForwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateForwardId";
|
||||
constexpr AZStd::string_view CameraTranslateBackwardIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateBackwardId";
|
||||
constexpr AZStd::string_view CameraTranslateLeftIdSetting = "/Amazon/Preferences/Editor/Camera/CameraTranslateLeftId";
|
||||
@@ -114,15 +115,15 @@ namespace SandboxEditor
|
||||
return AZStd::make_unique<EditorViewportSettingsCallbacksImpl>();
|
||||
}
|
||||
|
||||
AZ::Vector3 DefaultEditorCameraPosition()
|
||||
AZ::Vector3 CameraDefaultEditorPosition()
|
||||
{
|
||||
float xPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionX, 0.0));
|
||||
float yPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionY, -10.0));
|
||||
float zPosition = aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionZ, 4.0));
|
||||
return AZ::Vector3(xPosition, yPosition, zPosition);
|
||||
return AZ::Vector3(
|
||||
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionX, 0.0)),
|
||||
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionY, -10.0)),
|
||||
aznumeric_cast<float>(GetRegistry(CameraDefaultStartingPositionZ, 4.0)));
|
||||
}
|
||||
|
||||
void SetDefaultCameraEditorPosition(const AZ::Vector3 defaultCameraPosition)
|
||||
void SetCameraDefaultEditorPosition(const AZ::Vector3& defaultCameraPosition)
|
||||
{
|
||||
SetRegistry(CameraDefaultStartingPositionX, defaultCameraPosition.GetX());
|
||||
SetRegistry(CameraDefaultStartingPositionY, defaultCameraPosition.GetY());
|
||||
@@ -359,6 +360,16 @@ namespace SandboxEditor
|
||||
SetRegistry(CameraCaptureCursorLookSetting, capture);
|
||||
}
|
||||
|
||||
float CameraDefaultOrbitDistance()
|
||||
{
|
||||
return aznumeric_cast<float>(GetRegistry(CameraDefaultOrbitDistanceSetting, 20.0));
|
||||
}
|
||||
|
||||
void SetCameraDefaultOrbitDistance(const float distance)
|
||||
{
|
||||
SetRegistry(CameraDefaultOrbitDistanceSetting, distance);
|
||||
}
|
||||
|
||||
AzFramework::InputChannelId CameraTranslateForwardChannelId()
|
||||
{
|
||||
return AzFramework::InputChannelId(
|
||||
|
||||
@@ -33,9 +33,6 @@ namespace SandboxEditor
|
||||
//! event will fire when a value in the settings registry (editorpreferences.setreg) is modified.
|
||||
SANDBOX_API AZStd::unique_ptr<EditorViewportSettingsCallbacks> CreateEditorViewportSettingsCallbacks();
|
||||
|
||||
SANDBOX_API AZ::Vector3 DefaultEditorCameraPosition();
|
||||
SANDBOX_API void SetDefaultCameraEditorPosition(AZ::Vector3 defaultCameraPosition);
|
||||
|
||||
SANDBOX_API AZ::u64 MaxItemsShownInAssetBrowserSearch();
|
||||
SANDBOX_API void SetMaxItemsShownInAssetBrowserSearch(AZ::u64 numberOfItemsShown);
|
||||
|
||||
@@ -105,6 +102,12 @@ namespace SandboxEditor
|
||||
SANDBOX_API bool CameraCaptureCursorForLook();
|
||||
SANDBOX_API void SetCameraCaptureCursorForLook(bool capture);
|
||||
|
||||
SANDBOX_API AZ::Vector3 CameraDefaultEditorPosition();
|
||||
SANDBOX_API void SetCameraDefaultEditorPosition(const AZ::Vector3& position);
|
||||
|
||||
SANDBOX_API float CameraDefaultOrbitDistance();
|
||||
SANDBOX_API void SetCameraDefaultOrbitDistance(float distance);
|
||||
|
||||
SANDBOX_API AzFramework::InputChannelId CameraTranslateForwardChannelId();
|
||||
SANDBOX_API void SetCameraTranslateForwardChannelId(AZStd::string_view cameraTranslateForwardId);
|
||||
|
||||
|
||||
@@ -620,9 +620,9 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
break;
|
||||
|
||||
case eNotify_OnEndNewScene:
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
PopDisableRendering();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
|
||||
@@ -638,9 +638,9 @@ void EditorViewportWidget::OnEditorNotifyEvent(EEditorNotifyEvent event)
|
||||
break;
|
||||
|
||||
case eNotify_OnEndTerrainCreate:
|
||||
PopDisableRendering();
|
||||
|
||||
{
|
||||
PopDisableRendering();
|
||||
|
||||
Matrix34 viewTM;
|
||||
viewTM.SetIdentity();
|
||||
|
||||
@@ -2021,10 +2021,6 @@ void EditorViewportWidget::SetDefaultCamera()
|
||||
GetViewManager()->SetCameraObjectId(GUID_NULL);
|
||||
SetName(m_defaultViewName);
|
||||
|
||||
// Set the default Editor Camera position.
|
||||
m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(m_defaultViewTM);
|
||||
|
||||
// Synchronize the configured editor viewport FOV to the default camera
|
||||
if (m_viewPane)
|
||||
{
|
||||
@@ -2041,6 +2037,10 @@ void EditorViewportWidget::SetDefaultCamera()
|
||||
atomViewportRequests->PushView(contextName, m_defaultView);
|
||||
}
|
||||
|
||||
// Set the default Editor Camera position.
|
||||
m_defaultViewTM.SetTranslation(Vec3(m_editorViewportSettings.DefaultEditorCameraPosition()));
|
||||
SetViewTM(m_defaultViewTM);
|
||||
|
||||
PostCameraSet();
|
||||
}
|
||||
|
||||
@@ -2527,7 +2527,7 @@ bool EditorViewportSettings::StickySelectEnabled() const
|
||||
|
||||
AZ::Vector3 EditorViewportSettings::DefaultEditorCameraPosition() const
|
||||
{
|
||||
return SandboxEditor::DefaultEditorCameraPosition();
|
||||
return SandboxEditor::CameraDefaultEditorPosition();
|
||||
}
|
||||
|
||||
AZ_CVAR_EXTERNED(bool, ed_previewGameInFullscreen_once);
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
#include <AzFramework/XcbEventHandler.h>
|
||||
#include <AzFramework/XcbConnectionManager.h>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
namespace Editor
|
||||
@@ -23,16 +25,34 @@ namespace Editor
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
xcb_connection_t* EditorQtApplicationXcb::GetXcbConnectionFromQt()
|
||||
{
|
||||
QPlatformNativeInterface* native = platformNativeInterface();
|
||||
AZ_Warning("EditorQtApplicationXcb", native, "Unable to retrieve the native platform interface");
|
||||
if (!native)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<xcb_connection_t*>(native->nativeResourceForIntegration(QByteArray("connection")));
|
||||
}
|
||||
|
||||
void EditorQtApplicationXcb::OnStartPlayInEditor()
|
||||
{
|
||||
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
interface->SetEnableXInput(GetXcbConnectionFromQt(), true);
|
||||
}
|
||||
|
||||
void EditorQtApplicationXcb::OnStopPlayInEditor()
|
||||
{
|
||||
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
interface->SetEnableXInput(GetXcbConnectionFromQt(), false);
|
||||
}
|
||||
|
||||
bool EditorQtApplicationXcb::nativeEventFilter([[maybe_unused]] const QByteArray& eventType, void* message, long*)
|
||||
{
|
||||
if (GetIEditor()->IsInGameMode())
|
||||
{
|
||||
#ifdef PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
|
||||
// We need to handle RAW Input events in a separate loop. This is a workaround to enable XInput2 RAW Inputs using Editor mode.
|
||||
// TODO To have this call here might be not be perfect.
|
||||
AzFramework::XcbEventHandlerBus::Broadcast(&AzFramework::XcbEventHandler::PollSpecialEvents);
|
||||
|
||||
// Now handle the rest of the events.
|
||||
AzFramework::XcbEventHandlerBus::Broadcast(
|
||||
&AzFramework::XcbEventHandler::HandleXcbEvent, static_cast<xcb_generic_event_t*>(message));
|
||||
#endif
|
||||
|
||||
@@ -6,19 +6,35 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#if !defined(Q_MOC_RUN)
|
||||
#include <Editor/Core/QtEditorApplication.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
|
||||
#endif
|
||||
|
||||
using xcb_connection_t = struct xcb_connection_t;
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
class EditorQtApplicationXcb : public EditorQtApplication
|
||||
class EditorQtApplicationXcb
|
||||
: public EditorQtApplication
|
||||
, public AzToolsFramework::EditorEntityContextNotificationBus::Handler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
EditorQtApplicationXcb(int& argc, char** argv)
|
||||
: EditorQtApplication(argc, argv)
|
||||
{
|
||||
// Connect bus to listen for OnStart/StopPlayInEditor events
|
||||
AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
xcb_connection_t* GetXcbConnectionFromQt();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// AzToolsFramework::EditorEntityContextNotificationBus overrides
|
||||
void OnStartPlayInEditor() override;
|
||||
void OnStopPlayInEditor() override;
|
||||
|
||||
// QAbstractNativeEventFilter:
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
|
||||
};
|
||||
|
||||
@@ -806,12 +806,20 @@ namespace AzFramework
|
||||
[[maybe_unused]] float scrollDelta,
|
||||
[[maybe_unused]] float deltaTime)
|
||||
{
|
||||
const auto pivot = m_pivotFn();
|
||||
|
||||
if (!pivot.has_value())
|
||||
{
|
||||
EndActivation();
|
||||
return targetCamera;
|
||||
}
|
||||
|
||||
if (Beginning())
|
||||
{
|
||||
// as the camera starts, record the camera we would like to end up as
|
||||
m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation()));
|
||||
m_nextCamera.m_offset = m_offsetFn(pivot.value().GetDistance(targetCamera.Translation()));
|
||||
const auto angles =
|
||||
EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn())));
|
||||
EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), pivot.value())));
|
||||
m_nextCamera.m_pitch = angles.GetX();
|
||||
m_nextCamera.m_yaw = angles.GetZ();
|
||||
m_nextCamera.m_pivot = targetCamera.m_pivot;
|
||||
|
||||
@@ -651,7 +651,7 @@ namespace AzFramework
|
||||
class FocusCameraInput : public CameraInput
|
||||
{
|
||||
public:
|
||||
using PivotFn = AZStd::function<AZ::Vector3()>;
|
||||
using PivotFn = AZStd::function<AZStd::optional<AZ::Vector3>()>;
|
||||
|
||||
FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn);
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <AzFramework/XcbEventHandler.h>
|
||||
#include <AzFramework/XcbInterface.h>
|
||||
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -34,6 +36,31 @@ namespace AzFramework
|
||||
return m_xcbConnection.get();
|
||||
}
|
||||
|
||||
void SetEnableXInput(xcb_connection_t* connection, bool enable) override
|
||||
{
|
||||
struct Mask
|
||||
{
|
||||
xcb_input_event_mask_t head;
|
||||
xcb_input_xi_event_mask_t mask;
|
||||
};
|
||||
const Mask mask {
|
||||
/*.head=*/{
|
||||
/*.device_id=*/XCB_INPUT_DEVICE_ALL_MASTER,
|
||||
/*.mask_len=*/1
|
||||
},
|
||||
/*.mask=*/ enable ?
|
||||
(xcb_input_xi_event_mask_t)(XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE) :
|
||||
(xcb_input_xi_event_mask_t)XCB_NONE
|
||||
};
|
||||
|
||||
const xcb_setup_t* xcbSetup = xcb_get_setup(connection);
|
||||
const xcb_screen_t* xcbScreen = xcb_setup_roots_iterator(xcbSetup).data;
|
||||
|
||||
xcb_input_xi_select_events(connection, xcbScreen->root, 1, &mask.head);
|
||||
|
||||
xcb_flush(connection);
|
||||
}
|
||||
|
||||
private:
|
||||
XcbUniquePtr<xcb_connection_t, xcb_disconnect> m_xcbConnection = nullptr;
|
||||
};
|
||||
|
||||
@@ -24,6 +24,9 @@ namespace AzFramework
|
||||
virtual ~XcbConnectionManager() = default;
|
||||
|
||||
virtual xcb_connection_t* GetXcbConnection() const = 0;
|
||||
|
||||
//! Enables/Disables XInput Raw Input events.
|
||||
virtual void SetEnableXInput(xcb_connection_t* connection, bool enable) = 0;
|
||||
};
|
||||
|
||||
class XcbConnectionManagerBusTraits
|
||||
|
||||
@@ -23,9 +23,6 @@ namespace AzFramework
|
||||
virtual ~XcbEventHandler() = default;
|
||||
|
||||
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
|
||||
|
||||
// ATTN This is used as a workaround for RAW Input events when using the Editor.
|
||||
virtual void PollSpecialEvents(){};
|
||||
};
|
||||
|
||||
class XcbEventHandlerBusTraits : public AZ::EBusTraits
|
||||
|
||||
+97
-180
@@ -13,21 +13,68 @@
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
xcb_window_t GetSystemCursorFocusWindow()
|
||||
xcb_window_t GetSystemCursorFocusWindow(xcb_connection_t* connection)
|
||||
{
|
||||
void* systemCursorFocusWindow = nullptr;
|
||||
AzFramework::InputSystemCursorConstraintRequestBus::BroadcastResult(
|
||||
systemCursorFocusWindow, &AzFramework::InputSystemCursorConstraintRequests::GetSystemCursorConstraintWindow);
|
||||
|
||||
if (!systemCursorFocusWindow)
|
||||
if (systemCursorFocusWindow)
|
||||
{
|
||||
return XCB_NONE;
|
||||
return static_cast<xcb_window_t>(reinterpret_cast<uint64_t>(systemCursorFocusWindow));
|
||||
}
|
||||
|
||||
// TODO Clang compile error because cast .... loses information. On GNU/Linux HWND is void* and on 64-bit
|
||||
// machines its obviously 64 bit but we receive the window id from m_renderOverlay.winId() which is xcb_window_t 32-bit.
|
||||
// EWMH-compliant window managers set the "_NET_ACTIVE_WINDOW" property
|
||||
// of the X server's root window to the currently active window. This
|
||||
// retrieves value of that property.
|
||||
|
||||
return static_cast<xcb_window_t>(reinterpret_cast<uint64_t>(systemCursorFocusWindow));
|
||||
// Get the atom for the _NET_ACTIVE_WINDOW property
|
||||
constexpr int propertyNameLength = 18;
|
||||
xcb_generic_error_t* error = nullptr;
|
||||
XcbStdFreePtr<xcb_intern_atom_reply_t> activeWindowAtom {xcb_intern_atom_reply(
|
||||
connection,
|
||||
xcb_intern_atom(connection, /*only_if_exists=*/ 1, propertyNameLength, "_NET_ACTIVE_WINDOW"),
|
||||
&error
|
||||
)};
|
||||
if (!activeWindowAtom || error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "Retrieving _NET_ACTIVE_WINDOW atom failed : Error code %d", error->error_code);
|
||||
free(error);
|
||||
}
|
||||
return XCB_WINDOW_NONE;
|
||||
}
|
||||
|
||||
// Get the root window
|
||||
const xcb_window_t rootWId = xcb_setup_roots_iterator(xcb_get_setup(connection)).data->root;
|
||||
|
||||
// Fetch the value of the root window's _NET_ACTIVE_WINDOW property
|
||||
XcbStdFreePtr<xcb_get_property_reply_t> property {xcb_get_property_reply(
|
||||
connection,
|
||||
xcb_get_property(
|
||||
/*c=*/connection,
|
||||
/*_delete=*/ 0,
|
||||
/*window=*/rootWId,
|
||||
/*property=*/activeWindowAtom->atom,
|
||||
/*type=*/XCB_ATOM_WINDOW,
|
||||
/*long_offset=*/0,
|
||||
/*long_length=*/1
|
||||
),
|
||||
&error
|
||||
)};
|
||||
|
||||
if (!property || error)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "Retrieving _NET_ACTIVE_WINDOW atom failed : Error code %d", error->error_code);
|
||||
free(error);
|
||||
}
|
||||
return XCB_WINDOW_NONE;
|
||||
}
|
||||
|
||||
return *static_cast<xcb_window_t*>(xcb_get_property_value(property.get()));
|
||||
}
|
||||
|
||||
xcb_connection_t* XcbInputDeviceMouse::s_xcbConnection = nullptr;
|
||||
@@ -39,8 +86,7 @@ namespace AzFramework
|
||||
: InputDeviceMouse::Implementation(inputDevice)
|
||||
, m_systemCursorState(SystemCursorState::Unknown)
|
||||
, m_systemCursorPositionNormalized(0.5f, 0.5f)
|
||||
, m_prevConstraintWindow(XCB_NONE)
|
||||
, m_focusWindow(XCB_NONE)
|
||||
, m_focusWindow(XCB_WINDOW_NONE)
|
||||
, m_cursorShown(true)
|
||||
{
|
||||
XcbEventHandlerBus::Handler::BusConnect();
|
||||
@@ -57,14 +103,14 @@ namespace AzFramework
|
||||
|
||||
InputDeviceMouse::Implementation* XcbInputDeviceMouse::Create(InputDeviceMouse& inputDevice)
|
||||
{
|
||||
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
const auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
if (!interface)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB interface not available");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s_xcbConnection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection();
|
||||
s_xcbConnection = interface->GetXcbConnection();
|
||||
if (!s_xcbConnection)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "XCB connection not available");
|
||||
@@ -126,7 +172,7 @@ namespace AzFramework
|
||||
|
||||
// Get window information.
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xcbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) };
|
||||
|
||||
if (!xcbGeometryReply)
|
||||
{
|
||||
@@ -137,7 +183,7 @@ namespace AzFramework
|
||||
xcb_translate_coordinates(s_xcbConnection, window, s_xcbScreen->root, 0, 0);
|
||||
|
||||
const XcbStdFreePtr<xcb_translate_coordinates_reply_t> xkbTranslateCoordReply{ xcb_translate_coordinates_reply(
|
||||
s_xcbConnection, translate_coord, NULL) };
|
||||
s_xcbConnection, translate_coord, nullptr) };
|
||||
|
||||
if (!xkbTranslateCoordReply)
|
||||
{
|
||||
@@ -173,11 +219,11 @@ namespace AzFramework
|
||||
for (const auto& barrier : m_activeBarriers)
|
||||
{
|
||||
xcb_void_cookie_t cookie = xcb_xfixes_create_pointer_barrier_checked(
|
||||
s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, NULL);
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
s_xcbConnection, barrier.id, window, barrier.x0, barrier.y0, barrier.x1, barrier.y1, barrier.direction, 0, nullptr);
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xcbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
|
||||
AZ_Warning(
|
||||
"XcbInput", !xkbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0,
|
||||
"XcbInput", !xcbError, "XFixes, failed to create barrier %d at (%d %d %d %d)", barrier.id, barrier.x0, barrier.y0,
|
||||
barrier.x1, barrier.y1);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +253,7 @@ namespace AzFramework
|
||||
|
||||
const xcb_xfixes_query_version_cookie_t query_cookie = xcb_xfixes_query_version(s_xcbConnection, 5, 0);
|
||||
|
||||
xcb_generic_error_t* error = NULL;
|
||||
xcb_generic_error_t* error = nullptr;
|
||||
const XcbStdFreePtr<xcb_xfixes_query_version_reply_t> xkbQueryRequestReply{ xcb_xfixes_query_version_reply(
|
||||
s_xcbConnection, query_cookie, &error) };
|
||||
|
||||
@@ -244,7 +290,7 @@ namespace AzFramework
|
||||
|
||||
const xcb_input_xi_query_version_cookie_t query_version_cookie = xcb_input_xi_query_version(s_xcbConnection, 2, 2);
|
||||
|
||||
xcb_generic_error_t* error = NULL;
|
||||
xcb_generic_error_t* error = nullptr;
|
||||
const XcbStdFreePtr<xcb_input_xi_query_version_reply_t> xkbQueryRequestReply{ xcb_input_xi_query_version_reply(
|
||||
s_xcbConnection, query_version_cookie, &error) };
|
||||
|
||||
@@ -268,40 +314,13 @@ namespace AzFramework
|
||||
return m_xInputInitialized;
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::SetEnableXInput(bool enable)
|
||||
{
|
||||
struct
|
||||
{
|
||||
xcb_input_event_mask_t head;
|
||||
int mask;
|
||||
} mask;
|
||||
|
||||
mask.head.deviceid = XCB_INPUT_DEVICE_ALL;
|
||||
mask.head.mask_len = 1;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS |
|
||||
XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE | XCB_INPUT_XI_EVENT_MASK_MOTION | XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
|
||||
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask.mask = XCB_NONE;
|
||||
}
|
||||
|
||||
xcb_input_xi_select_events(s_xcbConnection, s_xcbScreen->root, 1, &mask.head);
|
||||
|
||||
xcb_flush(s_xcbConnection);
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::SetSystemCursorState(SystemCursorState systemCursorState)
|
||||
{
|
||||
if (systemCursorState != m_systemCursorState)
|
||||
{
|
||||
m_systemCursorState = systemCursorState;
|
||||
|
||||
m_focusWindow = GetSystemCursorFocusWindow();
|
||||
m_focusWindow = GetSystemCursorFocusWindow(s_xcbConnection);
|
||||
|
||||
HandleCursorState(m_focusWindow, systemCursorState);
|
||||
}
|
||||
@@ -309,52 +328,10 @@ namespace AzFramework
|
||||
|
||||
void XcbInputDeviceMouse::HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState)
|
||||
{
|
||||
bool confined = false, cursorShown = true;
|
||||
switch (systemCursorState)
|
||||
{
|
||||
case SystemCursorState::ConstrainedAndHidden:
|
||||
{
|
||||
//!< Constrained to the application's main window and hidden
|
||||
confined = true;
|
||||
cursorShown = false;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::ConstrainedAndVisible:
|
||||
{
|
||||
//!< Constrained to the application's main window and visible
|
||||
confined = true;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::UnconstrainedAndHidden:
|
||||
{
|
||||
//!< Free to move outside the main window but hidden while inside
|
||||
cursorShown = false;
|
||||
}
|
||||
break;
|
||||
case SystemCursorState::UnconstrainedAndVisible:
|
||||
{
|
||||
//!< Free to move outside the application's main window and visible
|
||||
}
|
||||
case SystemCursorState::Unknown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// ATTN GetSystemCursorFocusWindow when getting out of the play in editor will return XCB_NONE
|
||||
// We need however the window id to reset the cursor.
|
||||
if (XCB_NONE == window && (confined || cursorShown))
|
||||
{
|
||||
// Reuse the previous window to reset states.
|
||||
window = m_prevConstraintWindow;
|
||||
m_prevConstraintWindow = XCB_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remember the window we used to modify cursor and barrier states.
|
||||
m_prevConstraintWindow = window;
|
||||
}
|
||||
|
||||
SetEnableXInput(!cursorShown);
|
||||
const bool confined = (systemCursorState == SystemCursorState::ConstrainedAndHidden) ||
|
||||
(systemCursorState == SystemCursorState::ConstrainedAndVisible);
|
||||
const bool cursorShown = (systemCursorState == SystemCursorState::ConstrainedAndVisible) ||
|
||||
(systemCursorState == SystemCursorState::UnconstrainedAndVisible);
|
||||
|
||||
CreateBarriers(window, confined);
|
||||
ShowCursor(window, cursorShown);
|
||||
@@ -368,26 +345,26 @@ namespace AzFramework
|
||||
void XcbInputDeviceMouse::SetSystemCursorPositionNormalizedInternal(xcb_window_t window, AZ::Vector2 positionNormalized)
|
||||
{
|
||||
// TODO Basically not done at all. Added only the basic functions needed.
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xkbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xcbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) };
|
||||
|
||||
if (!xkbGeometryReply)
|
||||
if (!xcbGeometryReply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int16_t x = static_cast<int16_t>(positionNormalized.GetX() * xkbGeometryReply->width);
|
||||
const int16_t y = static_cast<int16_t>(positionNormalized.GetY() * xkbGeometryReply->height);
|
||||
const int16_t x = static_cast<int16_t>(positionNormalized.GetX() * xcbGeometryReply->width);
|
||||
const int16_t y = static_cast<int16_t>(positionNormalized.GetY() * xcbGeometryReply->height);
|
||||
|
||||
xcb_warp_pointer(s_xcbConnection, XCB_NONE, window, 0, 0, 0, 0, x, y);
|
||||
xcb_warp_pointer(s_xcbConnection, XCB_WINDOW_NONE, window, 0, 0, 0, 0, x, y);
|
||||
|
||||
xcb_flush(s_xcbConnection);
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized)
|
||||
{
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow();
|
||||
if (XCB_NONE == window)
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow(s_xcbConnection);
|
||||
if (XCB_WINDOW_NONE == window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -401,7 +378,7 @@ namespace AzFramework
|
||||
|
||||
const xcb_query_pointer_cookie_t pointer = xcb_query_pointer(s_xcbConnection, window);
|
||||
|
||||
const XcbStdFreePtr<xcb_query_pointer_reply_t> xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, NULL) };
|
||||
const XcbStdFreePtr<xcb_query_pointer_reply_t> xkbQueryPointerReply{ xcb_query_pointer_reply(s_xcbConnection, pointer, nullptr) };
|
||||
|
||||
if (!xkbQueryPointerReply)
|
||||
{
|
||||
@@ -409,7 +386,7 @@ namespace AzFramework
|
||||
}
|
||||
|
||||
const XcbStdFreePtr<xcb_get_geometry_reply_t> xkbGeometryReply{ xcb_get_geometry_reply(
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), NULL) };
|
||||
s_xcbConnection, xcb_get_geometry(s_xcbConnection, window), nullptr) };
|
||||
|
||||
if (!xkbGeometryReply)
|
||||
{
|
||||
@@ -429,8 +406,8 @@ namespace AzFramework
|
||||
|
||||
AZ::Vector2 XcbInputDeviceMouse::GetSystemCursorPositionNormalized() const
|
||||
{
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow();
|
||||
if (XCB_NONE == window)
|
||||
const xcb_window_t window = GetSystemCursorFocusWindow(s_xcbConnection);
|
||||
if (XCB_WINDOW_NONE == window)
|
||||
{
|
||||
return AZ::Vector2::CreateZero();
|
||||
}
|
||||
@@ -455,11 +432,11 @@ namespace AzFramework
|
||||
cookie = xcb_xfixes_hide_cursor_checked(s_xcbConnection, window);
|
||||
}
|
||||
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xkbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
const XcbStdFreePtr<xcb_generic_error_t> xcbError{ xcb_request_check(s_xcbConnection, cookie) };
|
||||
|
||||
if (xkbError)
|
||||
if (xcbError)
|
||||
{
|
||||
AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xkbError->error_code);
|
||||
AZ_Warning("XcbInput", false, "ShowCursor failed: %d", xcbError->error_code);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -500,14 +477,6 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::HandlePointerMotionEvents(const xcb_generic_event_t* event)
|
||||
{
|
||||
const xcb_input_motion_event_t* mouseMotionEvent = reinterpret_cast<const xcb_input_motion_event_t*>(event);
|
||||
|
||||
m_systemCursorPosition[0] = mouseMotionEvent->event_x;
|
||||
m_systemCursorPosition[1] = mouseMotionEvent->event_y;
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::HandleRawInputEvents(const xcb_ge_generic_event_t* event)
|
||||
{
|
||||
const xcb_ge_generic_event_t* genericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(event);
|
||||
@@ -552,78 +521,20 @@ namespace AzFramework
|
||||
}
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::PollSpecialEvents()
|
||||
{
|
||||
while (xcb_generic_event_t* genericEvent = xcb_poll_for_queued_event(s_xcbConnection))
|
||||
{
|
||||
// TODO Is the following correct? If we are showing the cursor, don't poll RAW Input events.
|
||||
switch (genericEvent->response_type & ~0x80)
|
||||
{
|
||||
case XCB_GE_GENERIC:
|
||||
{
|
||||
const xcb_ge_generic_event_t* geGenericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(genericEvent);
|
||||
|
||||
// Only handle raw inputs if we have focus.
|
||||
// Handle Raw Input events first.
|
||||
if ((geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) ||
|
||||
(geGenericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) ||
|
||||
(geGenericEvent->event_type == XCB_INPUT_RAW_MOTION))
|
||||
{
|
||||
HandleRawInputEvents(geGenericEvent);
|
||||
|
||||
free(genericEvent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XcbInputDeviceMouse::HandleXcbEvent(xcb_generic_event_t* event)
|
||||
{
|
||||
switch (event->response_type & ~0x80)
|
||||
{
|
||||
// QT5 is using by default XInput which means we do need to check for XCB_GE_GENERIC event to parse all mouse related events.
|
||||
// XInput raw events are sent from the server as a XCB_GE_GENERIC
|
||||
// event. A XCB_GE_GENERIC event is typecast to a
|
||||
// xcb_ge_generic_event_t, which is distinct from a
|
||||
// xcb_generic_event_t, and exists so that X11 extensions can extend
|
||||
// the event emission beyond the size that a normal X11 event could
|
||||
// contain.
|
||||
case XCB_GE_GENERIC:
|
||||
{
|
||||
const xcb_ge_generic_event_t* genericEvent = reinterpret_cast<const xcb_ge_generic_event_t*>(event);
|
||||
|
||||
// Handling RAW Inputs here works in GameMode but not in Editor mode because QT is
|
||||
// not handling RAW input events and passing to.
|
||||
if (!m_cursorShown)
|
||||
{
|
||||
// Handle Raw Input events first.
|
||||
if ((genericEvent->event_type == XCB_INPUT_RAW_BUTTON_PRESS) ||
|
||||
(genericEvent->event_type == XCB_INPUT_RAW_BUTTON_RELEASE) || (genericEvent->event_type == XCB_INPUT_RAW_MOTION))
|
||||
{
|
||||
HandleRawInputEvents(genericEvent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (genericEvent->event_type)
|
||||
{
|
||||
case XCB_INPUT_BUTTON_PRESS:
|
||||
{
|
||||
const xcb_input_button_press_event_t* mouseButtonEvent =
|
||||
reinterpret_cast<const xcb_input_button_press_event_t*>(genericEvent);
|
||||
HandleButtonPressEvents(mouseButtonEvent->detail, true);
|
||||
}
|
||||
break;
|
||||
case XCB_INPUT_BUTTON_RELEASE:
|
||||
{
|
||||
const xcb_input_button_release_event_t* mouseButtonEvent =
|
||||
reinterpret_cast<const xcb_input_button_release_event_t*>(genericEvent);
|
||||
HandleButtonPressEvents(mouseButtonEvent->detail, false);
|
||||
}
|
||||
break;
|
||||
case XCB_INPUT_MOTION:
|
||||
{
|
||||
HandlePointerMotionEvents(event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
HandleRawInputEvents(genericEvent);
|
||||
}
|
||||
break;
|
||||
case XCB_FOCUS_IN:
|
||||
@@ -634,6 +545,9 @@ namespace AzFramework
|
||||
m_focusWindow = focusInEvent->event;
|
||||
HandleCursorState(m_focusWindow, m_systemCursorState);
|
||||
}
|
||||
|
||||
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
interface->SetEnableXInput(interface->GetXcbConnection(), true);
|
||||
}
|
||||
break;
|
||||
case XCB_FOCUS_OUT:
|
||||
@@ -644,7 +558,10 @@ namespace AzFramework
|
||||
ProcessRawEventQueues();
|
||||
ResetInputChannelStates();
|
||||
|
||||
m_focusWindow = XCB_NONE;
|
||||
m_focusWindow = XCB_WINDOW_NONE;
|
||||
|
||||
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
|
||||
interface->SetEnableXInput(interface->GetXcbConnection(), false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -65,9 +65,6 @@ namespace AzFramework
|
||||
//! \ref AzFramework::InputDeviceMouse::Implementation::TickInputDevice
|
||||
void TickInputDevice() override;
|
||||
|
||||
//! This method is called by the Editor to accommodate some events with the Editor. Never called in Game mode.
|
||||
void PollSpecialEvents() override;
|
||||
|
||||
//! Handle X11 events.
|
||||
void HandleXcbEvent(xcb_generic_event_t* event) override;
|
||||
|
||||
@@ -77,9 +74,6 @@ namespace AzFramework
|
||||
//! Initialize XInput extension. Used for raw input during confinement and showing/hiding the cursor.
|
||||
static bool InitializeXInput();
|
||||
|
||||
//! Enables/Disables XInput Raw Input events.
|
||||
void SetEnableXInput(bool enable);
|
||||
|
||||
//! Create barriers.
|
||||
void CreateBarriers(xcb_window_t window, bool create);
|
||||
|
||||
@@ -98,9 +92,6 @@ namespace AzFramework
|
||||
//! Handle button press/release events.
|
||||
void HandleButtonPressEvents(uint32_t detail, bool pressed);
|
||||
|
||||
//! Handle motion notify events.
|
||||
void HandlePointerMotionEvents(const xcb_generic_event_t* event);
|
||||
|
||||
//! Will set cursor states and confinement modes.
|
||||
void HandleCursorState(xcb_window_t window, SystemCursorState systemCursorState);
|
||||
|
||||
@@ -160,7 +151,6 @@ namespace AzFramework
|
||||
AZ::Vector2 m_cursorHiddenPosition;
|
||||
|
||||
AZ::Vector2 m_systemCursorPositionNormalized;
|
||||
uint32_t m_systemCursorPosition[MAX_XI_RAW_AXIS];
|
||||
|
||||
static xcb_connection_t* s_xcbConnection;
|
||||
static xcb_screen_t* s_xcbScreen;
|
||||
@@ -171,9 +161,6 @@ namespace AzFramework
|
||||
//! Will be true if the xinput2 extension could be initialized.
|
||||
static bool m_xInputInitialized;
|
||||
|
||||
//! The window that had focus
|
||||
xcb_window_t m_prevConstraintWindow;
|
||||
|
||||
//! The current window that has focus
|
||||
xcb_window_t m_focusWindow;
|
||||
|
||||
|
||||
+19
-13
@@ -10,6 +10,8 @@
|
||||
#include <AzCore/IO/SystemFile.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
|
||||
#include <AzFramework/Process/ProcessWatcher.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -24,14 +26,20 @@ namespace AzFramework::AssetSystem::Platform
|
||||
AZ::IO::FixedMaxPath assetProcessorPath{ executableDirectory };
|
||||
// In Mac the Editor and game is within a bundle, so the path to the sibling app
|
||||
// has to go up from the Contents/MacOS folder the binary is in
|
||||
assetProcessorPath /= "../../../AssetProcessor.app";
|
||||
assetProcessorPath /= "../../../AssetProcessor.app/Contents/MacOS/AssetProcessor";
|
||||
assetProcessorPath = assetProcessorPath.LexicallyNormal();
|
||||
|
||||
if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str()))
|
||||
{
|
||||
// Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure.
|
||||
assetProcessorPath =
|
||||
AZ::IO::FixedMaxPath{engineRoot} / "bin" / AZ_TRAIT_OS_PLATFORM_NAME / AZ_BUILD_CONFIGURATION_TYPE / "AssetProcessor.app";
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath installedBinariesPath;
|
||||
settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
|
||||
{
|
||||
// Check for existence of one under a "bin" directory, i.e. engineRoot is an SDK structure.
|
||||
assetProcessorPath = AZ::IO::FixedMaxPath{ engineRoot } / installedBinariesPath / "AssetProcessor.app/Contents/MacOS/AssetProcessor";
|
||||
}
|
||||
}
|
||||
|
||||
if (!AZ::IO::SystemFile::Exists(assetProcessorPath.c_str()))
|
||||
{
|
||||
@@ -39,23 +47,21 @@ namespace AzFramework::AssetSystem::Platform
|
||||
}
|
||||
}
|
||||
|
||||
auto fullLaunchCommand = AZ::IO::FixedMaxPathString::format(R"(open -g "%s" --args --start-hidden)", assetProcessorPath.c_str());
|
||||
AZStd::string commandLineParams;
|
||||
// Add the engine path to the launch command if not empty
|
||||
if (!engineRoot.empty())
|
||||
{
|
||||
fullLaunchCommand += R"( --engine-path=")";
|
||||
fullLaunchCommand += engineRoot;
|
||||
fullLaunchCommand += '"';
|
||||
commandLineParams += AZStd::string::format("\"--engine-path=\"%s\"\"", engineRoot.data());
|
||||
}
|
||||
|
||||
// Add the active project path to the launch command if not empty
|
||||
if (!projectPath.empty())
|
||||
{
|
||||
fullLaunchCommand += R"( --project-path=")";
|
||||
fullLaunchCommand += projectPath;
|
||||
fullLaunchCommand += '"';
|
||||
commandLineParams += AZStd::string::format(" \"--regset=/Amazon/AzCore/Bootstrap/project_path=\"%s\"\"", projectPath.data());
|
||||
}
|
||||
|
||||
return system(fullLaunchCommand.c_str()) == 0;
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
processLaunchInfo.m_processExecutableString = AZStd::move(assetProcessorPath.Native());
|
||||
processLaunchInfo.m_commandlineParameters = commandLineParams;
|
||||
return AzFramework::ProcessLauncher::LaunchUnwatchedProcess(processLaunchInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
ACTION_TEMPLATE(ReturnMalloc,
|
||||
HAS_1_TEMPLATE_PARAMS(typename, T),
|
||||
AND_0_VALUE_PARAMS()) {
|
||||
T* value = static_cast<T*>(malloc(sizeof(T)));
|
||||
*value = T{};
|
||||
return value;
|
||||
}
|
||||
ACTION_TEMPLATE(ReturnMalloc,
|
||||
HAS_1_TEMPLATE_PARAMS(typename, T),
|
||||
AND_1_VALUE_PARAMS(p0)) {
|
||||
@@ -25,3 +32,38 @@ ACTION_TEMPLATE(ReturnMalloc,
|
||||
*value = T{ p0, p1 };
|
||||
return value;
|
||||
}
|
||||
ACTION_TEMPLATE(ReturnMalloc,
|
||||
HAS_1_TEMPLATE_PARAMS(typename, T),
|
||||
AND_3_VALUE_PARAMS(p0, p1, p2)) {
|
||||
T* value = static_cast<T*>(malloc(sizeof(T)));
|
||||
*value = T{ p0, p1, p2 };
|
||||
return value;
|
||||
}
|
||||
ACTION_TEMPLATE(ReturnMalloc,
|
||||
HAS_1_TEMPLATE_PARAMS(typename, T),
|
||||
AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
|
||||
T* value = static_cast<T*>(malloc(sizeof(T)));
|
||||
*value = T{ p0, p1, p2, p3 };
|
||||
return value;
|
||||
}
|
||||
ACTION_TEMPLATE(ReturnMalloc,
|
||||
HAS_1_TEMPLATE_PARAMS(typename, T),
|
||||
AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
|
||||
T* value = static_cast<T*>(malloc(sizeof(T)));
|
||||
*value = T{ p0, p1, p2, p3, p4 };
|
||||
return value;
|
||||
}
|
||||
ACTION_TEMPLATE(ReturnMalloc,
|
||||
HAS_1_TEMPLATE_PARAMS(typename, T),
|
||||
AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
|
||||
T* value = static_cast<T*>(malloc(sizeof(T)));
|
||||
*value = T{ p0, p1, p2, p3, p4, p5 };
|
||||
return value;
|
||||
}
|
||||
ACTION_TEMPLATE(ReturnMalloc,
|
||||
HAS_1_TEMPLATE_PARAMS(typename, T),
|
||||
AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
|
||||
T* value = static_cast<T*>(malloc(sizeof(T)));
|
||||
*value = T{ p0, p1, p2, p3, p4, p5, p6 };
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,82 @@ xcb_generic_error_t* xcb_request_check(xcb_connection_t* c, xcb_void_cookie_t co
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_request_check(c, cookie);
|
||||
}
|
||||
const xcb_setup_t* xcb_get_setup(xcb_connection_t *c)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_setup(c);
|
||||
}
|
||||
xcb_screen_iterator_t xcb_setup_roots_iterator(const xcb_setup_t* R)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_setup_roots_iterator(R);
|
||||
}
|
||||
const xcb_query_extension_reply_t* xcb_get_extension_data(xcb_connection_t* c, xcb_extension_t* ext)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_extension_data(c, ext);
|
||||
}
|
||||
int xcb_flush(xcb_connection_t *c)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_flush(c);
|
||||
}
|
||||
xcb_query_pointer_cookie_t xcb_query_pointer(xcb_connection_t* c, xcb_window_t window)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_query_pointer(c, window);
|
||||
}
|
||||
xcb_query_pointer_reply_t* xcb_query_pointer_reply(xcb_connection_t* c, xcb_query_pointer_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_query_pointer_reply(c, cookie, e);
|
||||
}
|
||||
xcb_get_geometry_cookie_t xcb_get_geometry(xcb_connection_t* c, xcb_drawable_t drawable)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_geometry(c, drawable);
|
||||
}
|
||||
xcb_get_geometry_reply_t* xcb_get_geometry_reply(xcb_connection_t* c, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_geometry_reply(c, cookie, e);
|
||||
}
|
||||
xcb_void_cookie_t xcb_warp_pointer(
|
||||
xcb_connection_t* c,
|
||||
xcb_window_t src_window,
|
||||
xcb_window_t dst_window,
|
||||
int16_t src_x,
|
||||
int16_t src_y,
|
||||
uint16_t src_width,
|
||||
uint16_t src_height,
|
||||
int16_t dst_x,
|
||||
int16_t dst_y)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_warp_pointer(c, src_window, dst_window, src_x, src_y, src_width, src_height, dst_x, dst_y);
|
||||
}
|
||||
xcb_intern_atom_cookie_t xcb_intern_atom(xcb_connection_t* c, uint8_t only_if_exists, uint16_t name_len, const char* name)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_intern_atom(c, only_if_exists, name_len, name);
|
||||
}
|
||||
xcb_intern_atom_reply_t* xcb_intern_atom_reply(xcb_connection_t* c, xcb_intern_atom_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_intern_atom_reply(c, cookie, e);
|
||||
}
|
||||
xcb_get_property_cookie_t xcb_get_property(
|
||||
xcb_connection_t* c,
|
||||
uint8_t _delete,
|
||||
xcb_window_t window,
|
||||
xcb_atom_t property,
|
||||
xcb_atom_t type,
|
||||
uint32_t long_offset,
|
||||
uint32_t long_length)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_property(c, _delete, window, property, type, long_offset, long_length);
|
||||
}
|
||||
xcb_get_property_reply_t* xcb_get_property_reply(xcb_connection_t* c, xcb_get_property_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_property_reply(c, cookie, e);
|
||||
}
|
||||
void* xcb_get_property_value(const xcb_get_property_reply_t* R)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_get_property_value(R);
|
||||
}
|
||||
uint32_t xcb_generate_id(xcb_connection_t *c)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_generate_id(c);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// xcb-xkb
|
||||
@@ -116,4 +192,76 @@ xkb_state_component xkb_state_update_mask(
|
||||
state, depressed_mods, latched_mods, locked_mods, depressed_layout, latched_layout, locked_layout);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// xcb-xfixes
|
||||
xcb_xfixes_query_version_cookie_t xcb_xfixes_query_version(
|
||||
xcb_connection_t* c, uint32_t client_major_version, uint32_t client_minor_version)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_query_version(c, client_major_version, client_minor_version);
|
||||
}
|
||||
xcb_xfixes_query_version_reply_t* xcb_xfixes_query_version_reply(
|
||||
xcb_connection_t* c, xcb_xfixes_query_version_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_query_version_reply(c, cookie, e);
|
||||
}
|
||||
xcb_void_cookie_t xcb_xfixes_show_cursor_checked(xcb_connection_t* c, xcb_window_t window)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_show_cursor_checked(c, window);
|
||||
}
|
||||
xcb_void_cookie_t xcb_xfixes_hide_cursor_checked(xcb_connection_t* c, xcb_window_t window)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_hide_cursor_checked(c, window);
|
||||
}
|
||||
xcb_void_cookie_t xcb_xfixes_delete_pointer_barrier_checked(xcb_connection_t* c, xcb_xfixes_barrier_t barrier)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_delete_pointer_barrier_checked(c, barrier);
|
||||
}
|
||||
xcb_translate_coordinates_cookie_t xcb_translate_coordinates(xcb_connection_t* c, xcb_window_t src_window, xcb_window_t dst_window, int16_t src_x, int16_t src_y)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_translate_coordinates(c, src_window, dst_window, src_x, src_y);
|
||||
}
|
||||
xcb_translate_coordinates_reply_t* xcb_translate_coordinates_reply(xcb_connection_t* c, xcb_translate_coordinates_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_translate_coordinates_reply(c, cookie, e);
|
||||
}
|
||||
xcb_void_cookie_t xcb_xfixes_create_pointer_barrier_checked(
|
||||
xcb_connection_t* c,
|
||||
xcb_xfixes_barrier_t barrier,
|
||||
xcb_window_t window,
|
||||
uint16_t x1,
|
||||
uint16_t y1,
|
||||
uint16_t x2,
|
||||
uint16_t y2,
|
||||
uint32_t directions,
|
||||
uint16_t num_devices,
|
||||
const uint16_t* devices)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_xfixes_create_pointer_barrier_checked(c, barrier, window, x1, y1, x2, y2, directions, num_devices, devices);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// xcb-xinput
|
||||
xcb_input_xi_query_version_cookie_t xcb_input_xi_query_version(xcb_connection_t* c, uint16_t major_version, uint16_t minor_version)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_input_xi_query_version(c, major_version, minor_version);
|
||||
}
|
||||
xcb_input_xi_query_version_reply_t* xcb_input_xi_query_version_reply(
|
||||
xcb_connection_t* c, xcb_input_xi_query_version_cookie_t cookie, xcb_generic_error_t** e)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_input_xi_query_version_reply(c, cookie, e);
|
||||
}
|
||||
xcb_void_cookie_t xcb_input_xi_select_events(
|
||||
xcb_connection_t* c, xcb_window_t window, uint16_t num_mask, const xcb_input_event_mask_t* masks)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_input_xi_select_events(c, window, num_mask, masks);
|
||||
}
|
||||
int xcb_input_raw_button_press_axisvalues_length (const xcb_input_raw_button_press_event_t *R)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_input_raw_button_press_axisvalues_length(R);
|
||||
}
|
||||
xcb_input_fp3232_t* xcb_input_raw_button_press_axisvalues_raw(const xcb_input_raw_button_press_event_t* R)
|
||||
{
|
||||
return MockXcbInterface::Instance()->xcb_input_raw_button_press_axisvalues_raw(R);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#undef explicit
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
#include "Printers.h"
|
||||
|
||||
@@ -62,6 +64,37 @@ public:
|
||||
MOCK_CONST_METHOD1(xcb_disconnect, void(xcb_connection_t* c));
|
||||
MOCK_CONST_METHOD1(xcb_poll_for_event, xcb_generic_event_t*(xcb_connection_t* c));
|
||||
MOCK_CONST_METHOD2(xcb_request_check, xcb_generic_error_t*(xcb_connection_t* c, xcb_void_cookie_t cookie));
|
||||
MOCK_CONST_METHOD1(xcb_get_setup, const xcb_setup_t*(xcb_connection_t *c));
|
||||
MOCK_CONST_METHOD1(xcb_setup_roots_iterator, xcb_screen_iterator_t(const xcb_setup_t* R));
|
||||
MOCK_CONST_METHOD2(xcb_get_extension_data, const xcb_query_extension_reply_t*(xcb_connection_t* c, xcb_extension_t* ext));
|
||||
MOCK_CONST_METHOD1(xcb_flush, int(xcb_connection_t *c));
|
||||
MOCK_CONST_METHOD2(xcb_query_pointer, xcb_query_pointer_cookie_t(xcb_connection_t* c, xcb_window_t window));
|
||||
MOCK_CONST_METHOD3(xcb_query_pointer_reply, xcb_query_pointer_reply_t*(xcb_connection_t* c, xcb_query_pointer_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD2(xcb_get_geometry, xcb_get_geometry_cookie_t(xcb_connection_t* c, xcb_drawable_t drawable));
|
||||
MOCK_CONST_METHOD3(xcb_get_geometry_reply, xcb_get_geometry_reply_t*(xcb_connection_t* c, xcb_get_geometry_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD9(xcb_warp_pointer, xcb_void_cookie_t(
|
||||
xcb_connection_t* c,
|
||||
xcb_window_t src_window,
|
||||
xcb_window_t dst_window,
|
||||
int16_t src_x,
|
||||
int16_t src_y,
|
||||
uint16_t src_width,
|
||||
uint16_t src_height,
|
||||
int16_t dst_x,
|
||||
int16_t dst_y));
|
||||
MOCK_CONST_METHOD4(xcb_intern_atom, xcb_intern_atom_cookie_t(xcb_connection_t* c, uint8_t only_if_exists, uint16_t name_len, const char* name));
|
||||
MOCK_CONST_METHOD3(xcb_intern_atom_reply, xcb_intern_atom_reply_t*(xcb_connection_t* c, xcb_intern_atom_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD7(xcb_get_property, xcb_get_property_cookie_t(
|
||||
xcb_connection_t* c,
|
||||
uint8_t _delete,
|
||||
xcb_window_t window,
|
||||
xcb_atom_t property,
|
||||
xcb_atom_t type,
|
||||
uint32_t long_offset,
|
||||
uint32_t long_length));
|
||||
MOCK_CONST_METHOD3(xcb_get_property_reply, xcb_get_property_reply_t*(xcb_connection_t* c, xcb_get_property_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD1(xcb_get_property_value, void*(const xcb_get_property_reply_t* R));
|
||||
MOCK_CONST_METHOD1(xcb_generate_id, uint32_t(xcb_connection_t *c));
|
||||
|
||||
// xcb-xkb
|
||||
MOCK_CONST_METHOD3(xcb_xkb_use_extension, xcb_xkb_use_extension_cookie_t(xcb_connection_t* c, uint16_t wantedMajor, uint16_t wantedMinor));
|
||||
@@ -83,6 +116,33 @@ public:
|
||||
MOCK_CONST_METHOD4(xkb_state_key_get_utf8, int(xkb_state* state, xkb_keycode_t key, char* buffer, size_t size));
|
||||
MOCK_CONST_METHOD7(xkb_state_update_mask, xkb_state_component(xkb_state* state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout));
|
||||
|
||||
// xcb-xfixes
|
||||
MOCK_CONST_METHOD3(xcb_xfixes_query_version, xcb_xfixes_query_version_cookie_t(xcb_connection_t* c, uint32_t client_major_version, uint32_t client_minor_version));
|
||||
MOCK_CONST_METHOD3(xcb_xfixes_query_version_reply, xcb_xfixes_query_version_reply_t*(xcb_connection_t* c, xcb_xfixes_query_version_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD2(xcb_xfixes_show_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window));
|
||||
MOCK_CONST_METHOD2(xcb_xfixes_hide_cursor_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window));
|
||||
MOCK_CONST_METHOD2(xcb_xfixes_delete_pointer_barrier_checked, xcb_void_cookie_t(xcb_connection_t* c, xcb_xfixes_barrier_t barrier));
|
||||
MOCK_CONST_METHOD5(xcb_translate_coordinates, xcb_translate_coordinates_cookie_t(xcb_connection_t* c, xcb_window_t src_window, xcb_window_t dst_window, int16_t src_x, int16_t src_y));
|
||||
MOCK_CONST_METHOD3(xcb_translate_coordinates_reply, xcb_translate_coordinates_reply_t*(xcb_connection_t* c, xcb_translate_coordinates_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD10(xcb_xfixes_create_pointer_barrier_checked, xcb_void_cookie_t(
|
||||
xcb_connection_t* c,
|
||||
xcb_xfixes_barrier_t barrier,
|
||||
xcb_window_t window,
|
||||
uint16_t x1,
|
||||
uint16_t y1,
|
||||
uint16_t x2,
|
||||
uint16_t y2,
|
||||
uint32_t directions,
|
||||
uint16_t num_devices,
|
||||
const uint16_t* devices));
|
||||
|
||||
// xcb-xinput
|
||||
MOCK_CONST_METHOD3(xcb_input_xi_query_version, xcb_input_xi_query_version_cookie_t(xcb_connection_t* c, uint16_t major_version, uint16_t minor_version));
|
||||
MOCK_CONST_METHOD3(xcb_input_xi_query_version_reply, xcb_input_xi_query_version_reply_t*(xcb_connection_t* c, xcb_input_xi_query_version_cookie_t cookie, xcb_generic_error_t** e));
|
||||
MOCK_CONST_METHOD4(xcb_input_xi_select_events, xcb_void_cookie_t(xcb_connection_t* c, xcb_window_t window, uint16_t num_mask, const xcb_input_event_mask_t* masks));
|
||||
MOCK_CONST_METHOD1(xcb_input_raw_button_press_axisvalues_length, int(const xcb_input_raw_button_press_event_t* R));
|
||||
MOCK_CONST_METHOD1(xcb_input_raw_button_press_axisvalues_raw, xcb_input_fp3232_t*(const xcb_input_raw_button_press_event_t* R));
|
||||
|
||||
private:
|
||||
static inline MockXcbInterface* self = nullptr;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,12 @@ namespace AzFramework
|
||||
public:
|
||||
void SetUp() override;
|
||||
|
||||
template<typename T>
|
||||
static xcb_generic_event_t MakeEvent(T event)
|
||||
{
|
||||
return *reinterpret_cast<xcb_generic_event_t*>(&event);
|
||||
}
|
||||
|
||||
protected:
|
||||
testing::NiceMock<MockXcbInterface> m_interface;
|
||||
xcb_connection_t m_connection{};
|
||||
|
||||
@@ -21,12 +21,6 @@
|
||||
#include "XcbBaseTestFixture.h"
|
||||
#include "XcbTestApplication.h"
|
||||
|
||||
template<typename T>
|
||||
xcb_generic_event_t MakeEvent(T event)
|
||||
{
|
||||
return *reinterpret_cast<xcb_generic_event_t*>(&event);
|
||||
}
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
// Sets up default behavior for mock keyboard responses to xcb methods
|
||||
|
||||
@@ -0,0 +1,545 @@
|
||||
/*
|
||||
* 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 <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
|
||||
#include "XcbBaseTestFixture.h"
|
||||
#include "XcbTestApplication.h"
|
||||
#include "Matchers.h"
|
||||
#include "Actions.h"
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
// Sets up default behavior for mock keyboard responses to xcb methods
|
||||
class XcbInputDeviceMouseTests
|
||||
: public XcbBaseTestFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
using testing::Eq;
|
||||
using testing::Field;
|
||||
using testing::Return;
|
||||
using testing::StrEq;
|
||||
using testing::_;
|
||||
|
||||
XcbBaseTestFixture::SetUp();
|
||||
|
||||
ON_CALL(m_interface, xcb_get_setup(&m_connection))
|
||||
.WillByDefault(Return(&s_xcbSetup));
|
||||
ON_CALL(m_interface, xcb_setup_roots_iterator(&s_xcbSetup))
|
||||
.WillByDefault(Return(xcb_screen_iterator_t{&s_xcbScreen}));
|
||||
|
||||
ON_CALL(m_interface, xcb_get_extension_data(&m_connection, &xcb_xfixes_id))
|
||||
.WillByDefault(Return(&s_xfixesExtensionReply));
|
||||
ON_CALL(m_interface, xcb_xfixes_query_version_reply(&m_connection, _, _))
|
||||
.WillByDefault(ReturnMalloc<xcb_xfixes_query_version_reply_t>(
|
||||
/*response_type=*/(uint8_t)XCB_XFIXES_QUERY_VERSION,
|
||||
/*pad0=*/(uint8_t)0,
|
||||
/*sequence=*/(uint16_t)1,
|
||||
/*length=*/0u,
|
||||
/*major_version=*/5u,
|
||||
/*minor_version=*/0u
|
||||
));
|
||||
|
||||
ON_CALL(m_interface, xcb_get_extension_data(&m_connection, &xcb_input_id))
|
||||
.WillByDefault(Return(&s_xfixesExtensionReply));
|
||||
ON_CALL(m_interface, xcb_input_xi_query_version_reply(&m_connection, _, _))
|
||||
.WillByDefault(ReturnMalloc<xcb_input_xi_query_version_reply_t>(
|
||||
/*response_type=*/(uint8_t)XCB_INPUT_XI_QUERY_VERSION,
|
||||
/*pad0=*/(uint8_t)0,
|
||||
/*sequence=*/(uint16_t)1,
|
||||
/*length=*/0u,
|
||||
/*major_version=*/(uint16_t)2,
|
||||
/*minor_version=*/(uint16_t)2
|
||||
));
|
||||
|
||||
// Set the default focus window
|
||||
EXPECT_CALL(m_interface, xcb_intern_atom(&m_connection, 1, 18, StrEq("_NET_ACTIVE_WINDOW")))
|
||||
.WillRepeatedly(Return(xcb_intern_atom_cookie_t{/*.sequence=*/ 1}));
|
||||
ON_CALL(m_interface, xcb_intern_atom_reply(&m_connection, Field(&xcb_intern_atom_cookie_t::sequence, Eq(1)), _))
|
||||
.WillByDefault(ReturnMalloc<xcb_intern_atom_reply_t>(
|
||||
/*response_type=*/(uint8_t)XCB_INTERN_ATOM,
|
||||
/*pad0=*/(uint8_t)0,
|
||||
/*sequence=*/(uint16_t)1,
|
||||
/*length=*/0u,
|
||||
/*xcb_atom_t=*/s_netActiveWindowAtom
|
||||
));
|
||||
ON_CALL(m_interface, xcb_get_property(&m_connection, 0, s_rootWindow, s_netActiveWindowAtom, XCB_ATOM_WINDOW, 0, 1))
|
||||
.WillByDefault(Return(xcb_get_property_cookie_t{/*.sequence=*/ s_getActiveWindowPropertySequence}));
|
||||
ON_CALL(m_interface, xcb_get_property_reply(&m_connection, Field(&xcb_get_property_cookie_t::sequence, Eq(s_getActiveWindowPropertySequence)), _))
|
||||
.WillByDefault(ReturnMalloc<xcb_get_property_reply_t>(
|
||||
/*response_type=*/(uint8_t)XCB_GET_PROPERTY,
|
||||
/*format=*/(uint8_t)0,
|
||||
/*sequence=*/(uint16_t)s_getActiveWindowPropertySequence,
|
||||
/*length=*/0u,
|
||||
/*type=*/XCB_ATOM_WINDOW,
|
||||
/*bytes_after=*/0u,
|
||||
/*value_len=*/1u
|
||||
));
|
||||
ON_CALL(m_interface, xcb_get_property_value(Field(&xcb_get_property_reply_t::sequence, Eq(s_getActiveWindowPropertySequence))))
|
||||
.WillByDefault(Return(const_cast<xcb_window_t*>(&s_nullWindow)));
|
||||
|
||||
ON_CALL(m_interface, xcb_get_geometry(&m_connection, _))
|
||||
.WillByDefault(Return(xcb_get_geometry_cookie_t{/*.sequence=*/1}));
|
||||
ON_CALL(m_interface, xcb_get_geometry_reply(&m_connection, Field(&xcb_get_geometry_cookie_t::sequence, Eq(1)), _))
|
||||
.WillByDefault(ReturnMalloc<xcb_get_geometry_reply_t>(s_defaultWindowGeometry));
|
||||
}
|
||||
|
||||
void PumpApplication()
|
||||
{
|
||||
m_application.PumpSystemEventLoopUntilEmpty();
|
||||
m_application.TickSystem();
|
||||
m_application.Tick();
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr inline uint8_t s_xinputMajorOpcode = 131;
|
||||
static constexpr inline xcb_window_t s_rootWindow = 1;
|
||||
static constexpr inline xcb_window_t s_nullWindow = XCB_WINDOW_NONE;
|
||||
static constexpr inline xcb_input_device_id_t s_virtualCorePointerId = 2;
|
||||
static constexpr inline xcb_input_device_id_t s_physicalPointerDeviceId = 3;
|
||||
static constexpr inline uint16_t s_screenWidthInPixels = 3840;
|
||||
static constexpr inline uint16_t s_screenHeightInPixels = 2160;
|
||||
static constexpr inline uint16_t s_getActiveWindowPropertySequence = 2160;
|
||||
static constexpr inline xcb_atom_t s_netActiveWindowAtom = 1;
|
||||
static constexpr inline xcb_setup_t s_xcbSetup{
|
||||
/*.status=*/1,
|
||||
/*.pad0=*/0,
|
||||
/*.protocol_major_version=*/11,
|
||||
/*.protocol_minor_version=*/0,
|
||||
};
|
||||
static inline xcb_screen_t s_xcbScreen{
|
||||
/*.root=*/s_rootWindow,
|
||||
/*.default_colormap=*/32,
|
||||
/*.white_pixel=*/16777215,
|
||||
/*.black_pixel=*/0,
|
||||
/*.current_input_masks=*/0,
|
||||
/*.width_in_pixels=*/s_screenWidthInPixels,
|
||||
/*.height_in_pixels=*/s_screenHeightInPixels,
|
||||
/*.width_in_millimeters=*/602,
|
||||
/*.height_in_millimeters=*/341,
|
||||
};
|
||||
static constexpr inline xcb_query_extension_reply_t s_xfixesExtensionReply{
|
||||
/*.response_type=*/XCB_QUERY_EXTENSION,
|
||||
/*.pad0=*/0,
|
||||
/*.sequence=*/1,
|
||||
/*.length=*/0,
|
||||
/*.present=*/1,
|
||||
};
|
||||
static constexpr inline xcb_query_extension_reply_t s_xinputExtensionReply{
|
||||
/*.response_type=*/XCB_QUERY_EXTENSION,
|
||||
/*.pad0=*/0,
|
||||
/*.sequence=*/1,
|
||||
/*.length=*/0,
|
||||
/*.present=*/1,
|
||||
/*.major_opcode=*/s_xinputMajorOpcode,
|
||||
};
|
||||
static constexpr inline xcb_get_geometry_reply_t s_defaultWindowGeometry{
|
||||
/*.response_type=*/XCB_GET_GEOMETRY,
|
||||
/*.depth=*/0,
|
||||
/*.sequence=*/1,
|
||||
/*.length=*/0,
|
||||
/*.root=*/s_rootWindow,
|
||||
/*.x=*/100,
|
||||
/*.y=*/100,
|
||||
/*.width=*/100,
|
||||
/*.height=*/100,
|
||||
/*.border_width=*/3,
|
||||
/*.pad0[2]=*/{},
|
||||
};
|
||||
XcbTestApplication m_application{
|
||||
/*enabledGamepadsCount=*/0,
|
||||
/*keyboardEnabled=*/false,
|
||||
/*motionEnabled=*/false,
|
||||
/*mouseEnabled=*/true,
|
||||
/*touchEnabled=*/false,
|
||||
/*virtualKeyboardEnabled=*/false
|
||||
};
|
||||
};
|
||||
|
||||
struct MouseButtonTestData
|
||||
{
|
||||
xcb_button_index_t m_button;
|
||||
};
|
||||
|
||||
class XcbInputDeviceMouseButtonTests
|
||||
: public XcbInputDeviceMouseTests
|
||||
, public testing::WithParamInterface<MouseButtonTestData>
|
||||
{
|
||||
public:
|
||||
static InputChannelId GetInputChannelIdForButton(const xcb_button_index_t button)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case XCB_BUTTON_INDEX_1:
|
||||
return InputDeviceMouse::Button::Left;
|
||||
case XCB_BUTTON_INDEX_2:
|
||||
return InputDeviceMouse::Button::Right;
|
||||
case XCB_BUTTON_INDEX_3:
|
||||
return InputDeviceMouse::Button::Middle;
|
||||
}
|
||||
return InputChannelId{};
|
||||
}
|
||||
|
||||
AZStd::array<InputChannelId, 4> GetIdleChannelIdsForButton(const xcb_button_index_t button)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case XCB_BUTTON_INDEX_1:
|
||||
return { InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 };
|
||||
case XCB_BUTTON_INDEX_2:
|
||||
return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 };
|
||||
case XCB_BUTTON_INDEX_3:
|
||||
return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Other1, InputDeviceMouse::Button::Other2 };
|
||||
case XCB_BUTTON_INDEX_4:
|
||||
return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other2 };
|
||||
case XCB_BUTTON_INDEX_5:
|
||||
return { InputDeviceMouse::Button::Left, InputDeviceMouse::Button::Right, InputDeviceMouse::Button::Middle, InputDeviceMouse::Button::Other1 };
|
||||
}
|
||||
return AZStd::array<InputChannelId, 4>();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(XcbInputDeviceMouseButtonTests, ButtonInputChannelsUpdateStateFromXcbEvents)
|
||||
{
|
||||
using testing::Each;
|
||||
using testing::Eq;
|
||||
using testing::NotNull;
|
||||
using testing::Property;
|
||||
using testing::Return;
|
||||
|
||||
// Set the expectations for the events that will be generated
|
||||
// nullptr entries represent when the event queue is empty, and will cause
|
||||
// PumpSystemEventLoopUntilEmpty to return
|
||||
//
|
||||
// Event pointers are freed by the calling code, so these actions
|
||||
// malloc new copies
|
||||
//
|
||||
// The xcb mouse does not react to the `XCB_BUTTON_PRESS` /
|
||||
// `XCB_BUTTON_RELEASE` events, but it will still receive those events
|
||||
// from the X server.
|
||||
EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection))
|
||||
.WillOnce(ReturnMalloc<xcb_generic_event_t>(MakeEvent(xcb_input_raw_button_press_event_t{
|
||||
/*response_type=*/XCB_GE_GENERIC,
|
||||
/*extension=*/s_xinputMajorOpcode,
|
||||
/*sequence=*/4,
|
||||
/*length=*/2,
|
||||
/*event_type=*/XCB_INPUT_RAW_BUTTON_PRESS,
|
||||
/*deviceid=*/s_virtualCorePointerId,
|
||||
/*time=*/3984920,
|
||||
/*detail=*/GetParam().m_button,
|
||||
/*sourceid=*/s_physicalPointerDeviceId,
|
||||
/*valuators_len=*/2,
|
||||
/*flags=*/0,
|
||||
/*pad0[4]=*/{},
|
||||
/*full_sequence=*/4
|
||||
})))
|
||||
.WillOnce(Return(nullptr))
|
||||
.WillOnce(ReturnMalloc<xcb_generic_event_t>(MakeEvent(xcb_button_press_event_t{
|
||||
/*response_type=*/XCB_BUTTON_PRESS,
|
||||
/*detail=*/static_cast<xcb_button_t>(GetParam().m_button),
|
||||
/*sequence=*/4,
|
||||
/*time=*/3984920,
|
||||
/*root=*/s_rootWindow,
|
||||
/*event=*/119537664,
|
||||
/*child=*/0,
|
||||
/*root_x=*/55,
|
||||
/*root_y=*/1099,
|
||||
/*event_x=*/55,
|
||||
/*event_y=*/55,
|
||||
/*state=*/0,
|
||||
/*same_screen=*/1
|
||||
})))
|
||||
.WillOnce(Return(nullptr))
|
||||
.WillOnce(ReturnMalloc<xcb_generic_event_t>(MakeEvent(xcb_input_raw_button_release_event_t{
|
||||
/*response_type=*/XCB_GE_GENERIC,
|
||||
/*extension=*/s_xinputMajorOpcode,
|
||||
/*sequence=*/4,
|
||||
/*length=*/2,
|
||||
/*event_type=*/XCB_INPUT_RAW_BUTTON_RELEASE,
|
||||
/*deviceid=*/s_virtualCorePointerId,
|
||||
/*time=*/3984964,
|
||||
/*detail=*/GetParam().m_button,
|
||||
/*sourceid=*/s_physicalPointerDeviceId,
|
||||
/*valuators_len=*/2,
|
||||
/*flags=*/0,
|
||||
/*pad0[4]=*/{},
|
||||
/*full_sequence=*/4
|
||||
})))
|
||||
.WillOnce(Return(nullptr))
|
||||
.WillOnce(ReturnMalloc<xcb_generic_event_t>(MakeEvent(xcb_button_release_event_t{
|
||||
/*response_type=*/XCB_BUTTON_RELEASE,
|
||||
/*detail=*/static_cast<xcb_button_t>(GetParam().m_button),
|
||||
/*sequence=*/4,
|
||||
/*time=*/3984964,
|
||||
/*root=*/s_rootWindow,
|
||||
/*event=*/119537664,
|
||||
/*child=*/0,
|
||||
/*root_x=*/55,
|
||||
/*root_y=*/1099,
|
||||
/*event_x=*/55,
|
||||
/*event_y=*/55,
|
||||
/*state=*/XCB_KEY_BUT_MASK_BUTTON_1,
|
||||
/*same_screen=*/1
|
||||
})))
|
||||
.WillOnce(Return(nullptr))
|
||||
;
|
||||
|
||||
m_application.Start();
|
||||
InputSystemCursorRequestBus::Event(
|
||||
InputDeviceMouse::Id,
|
||||
&InputSystemCursorRequests::SetSystemCursorState,
|
||||
SystemCursorState::ConstrainedAndHidden);
|
||||
|
||||
const InputChannel* activeButtonChannel = InputChannelRequests::FindInputChannel(GetInputChannelIdForButton(GetParam().m_button));
|
||||
const auto inactiveButtonChannels = [this]()
|
||||
{
|
||||
const auto inactiveButtonChannelIds = GetIdleChannelIdsForButton(GetParam().m_button);
|
||||
AZStd::array<const InputChannel*, 4> channels{};
|
||||
AZStd::transform(begin(inactiveButtonChannelIds), end(inactiveButtonChannelIds), begin(channels), [](const InputChannelId& id)
|
||||
{
|
||||
return InputChannelRequests::FindInputChannel(id);
|
||||
});
|
||||
return channels;
|
||||
}();
|
||||
|
||||
ASSERT_TRUE(activeButtonChannel);
|
||||
ASSERT_THAT(inactiveButtonChannels, Each(NotNull()));
|
||||
|
||||
EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Idle));
|
||||
EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle))));
|
||||
|
||||
PumpApplication();
|
||||
|
||||
EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Began));
|
||||
EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle))));
|
||||
|
||||
PumpApplication();
|
||||
|
||||
EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Updated));
|
||||
EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle))));
|
||||
|
||||
PumpApplication();
|
||||
|
||||
EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Ended));
|
||||
EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle))));
|
||||
|
||||
PumpApplication();
|
||||
|
||||
EXPECT_THAT(activeButtonChannel->GetState(), Eq(InputChannel::State::Idle));
|
||||
EXPECT_THAT(inactiveButtonChannels, Each(Property(&InputChannel::GetState, Eq(InputChannel::State::Idle))));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
AllButtons,
|
||||
XcbInputDeviceMouseButtonTests,
|
||||
testing::Values(
|
||||
MouseButtonTestData{ XCB_BUTTON_INDEX_1 },
|
||||
MouseButtonTestData{ XCB_BUTTON_INDEX_2 },
|
||||
MouseButtonTestData{ XCB_BUTTON_INDEX_3 }
|
||||
// XCB_BUTTON_INDEX_4 and XCB_BUTTON_INDEX_5 map to positive and
|
||||
// negative scroll wheel events, which are handled as motion events
|
||||
)
|
||||
);
|
||||
|
||||
TEST_F(XcbInputDeviceMouseTests, MovementInputChannelsUpdateStateFromXcbEvents)
|
||||
{
|
||||
using testing::Each;
|
||||
using testing::Eq;
|
||||
using testing::FloatEq;
|
||||
using testing::NotNull;
|
||||
using testing::Property;
|
||||
using testing::Return;
|
||||
|
||||
// Set the expectations for the events that will be generated
|
||||
// nullptr entries represent when the event queue is empty, and will cause
|
||||
// PumpSystemEventLoopUntilEmpty to return
|
||||
//
|
||||
// Event pointers are freed by the calling code, so these actions
|
||||
// malloc new copies
|
||||
//
|
||||
// The xcb mouse does not react to the `XCB_MOTION_NOTIFY` event, but
|
||||
// it will still receive it from the X server.
|
||||
EXPECT_CALL(m_interface, xcb_poll_for_event(&m_connection))
|
||||
.WillOnce(ReturnMalloc<xcb_generic_event_t>(MakeEvent(xcb_input_raw_motion_event_t{
|
||||
/*response_type=*/XCB_GE_GENERIC,
|
||||
/*extension=*/s_xinputMajorOpcode,
|
||||
/*sequence=*/5,
|
||||
/*length=*/10,
|
||||
/*event_type=*/XCB_INPUT_RAW_MOTION,
|
||||
/*deviceid=*/s_virtualCorePointerId,
|
||||
/*time=*/0, // use the time value to identify each event
|
||||
/*detail=*/XCB_MOTION_NORMAL,
|
||||
/*sourceid=*/s_physicalPointerDeviceId,
|
||||
/*valuators_len=*/2, // number of axes that have values for this event
|
||||
/*flags=*/0,
|
||||
/*pad0[4]=*/{},
|
||||
/*full_sequence=*/5,
|
||||
})))
|
||||
.WillOnce(Return(nullptr))
|
||||
.WillOnce(ReturnMalloc<xcb_generic_event_t>(MakeEvent(xcb_motion_notify_event_t{
|
||||
/*response_type=*/XCB_MOTION_NOTIFY,
|
||||
/*detail=*/XCB_MOTION_NORMAL,
|
||||
/*sequence=*/5,
|
||||
/*time=*/1, // use the time value to identify each event
|
||||
/*root=*/s_rootWindow,
|
||||
/*event=*/127926272,
|
||||
/*child=*/0,
|
||||
/*root_x=*/95,
|
||||
/*root_y=*/1079,
|
||||
/*event_x=*/95,
|
||||
/*event_y=*/20,
|
||||
/*state=*/0,
|
||||
/*same_screen=*/1,
|
||||
})))
|
||||
.WillOnce(Return(nullptr))
|
||||
;
|
||||
|
||||
AZStd::array axisValues
|
||||
{
|
||||
xcb_input_fp3232_t{ /*.integral=*/ 1, /*.fraction=*/0 }, // x motion
|
||||
xcb_input_fp3232_t{ /*.integral=*/ 2, /*.fraction=*/0 } // y motion
|
||||
};
|
||||
|
||||
EXPECT_CALL(m_interface, xcb_input_raw_button_press_axisvalues_length(testing::Field(&xcb_input_raw_button_press_event_t::time, 0)))
|
||||
.WillRepeatedly(testing::Return(2)); // x and y axis
|
||||
EXPECT_CALL(m_interface, xcb_input_raw_button_press_axisvalues_raw(testing::Field(&xcb_input_raw_button_press_event_t::time, 0)))
|
||||
.WillRepeatedly(testing::Return(axisValues.data())); // x and y axis
|
||||
|
||||
m_application.Start();
|
||||
InputSystemCursorRequestBus::Event(
|
||||
InputDeviceMouse::Id,
|
||||
&InputSystemCursorRequests::SetSystemCursorState,
|
||||
SystemCursorState::ConstrainedAndHidden);
|
||||
|
||||
const InputChannel* xMotionChannel = InputChannelRequests::FindInputChannel(InputDeviceMouse::Movement::X);
|
||||
const InputChannel* yMotionChannel = InputChannelRequests::FindInputChannel(InputDeviceMouse::Movement::Y);
|
||||
ASSERT_TRUE(xMotionChannel);
|
||||
ASSERT_TRUE(yMotionChannel);
|
||||
|
||||
EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Idle));
|
||||
EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Idle));
|
||||
EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(0.0f));
|
||||
EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(0.0f));
|
||||
|
||||
PumpApplication();
|
||||
|
||||
EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Began));
|
||||
EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Began));
|
||||
EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(1.0f));
|
||||
EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(2.0f));
|
||||
|
||||
PumpApplication();
|
||||
|
||||
EXPECT_THAT(xMotionChannel->GetState(), Eq(InputChannel::State::Ended));
|
||||
EXPECT_THAT(yMotionChannel->GetState(), Eq(InputChannel::State::Ended));
|
||||
EXPECT_THAT(xMotionChannel->GetValue(), FloatEq(0.0f));
|
||||
EXPECT_THAT(yMotionChannel->GetValue(), FloatEq(0.0f));
|
||||
}
|
||||
|
||||
struct GetCursorPositionParam
|
||||
{
|
||||
int16_t m_x;
|
||||
int16_t m_y;
|
||||
};
|
||||
|
||||
class XcbGetSystemCursorPositionTests
|
||||
: public XcbInputDeviceMouseTests
|
||||
, public testing::WithParamInterface<GetCursorPositionParam>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(XcbGetSystemCursorPositionTests, GetSystemCursorPositionNormalizedReturnsCorrectValue)
|
||||
{
|
||||
using testing::Eq;
|
||||
using testing::Field;
|
||||
using testing::Return;
|
||||
using testing::_;
|
||||
|
||||
xcb_window_t focusWindow = 42;
|
||||
const xcb_query_pointer_reply_t queryPointerReply{
|
||||
/*.response_type=*/XCB_QUERY_POINTER,
|
||||
/*.same_screen=*/1,
|
||||
/*.sequence=*/0,
|
||||
/*.length=*/1,
|
||||
/*.root=*/s_rootWindow,
|
||||
/*.child=*/focusWindow,
|
||||
/*.root_x=*/static_cast<int16_t>(GetParam().m_x + s_defaultWindowGeometry.x),
|
||||
/*.root_y=*/static_cast<int16_t>(GetParam().m_y + s_defaultWindowGeometry.y),
|
||||
/*.win_x=*/GetParam().m_x,
|
||||
/*.win_y=*/GetParam().m_y,
|
||||
/*.mask=*/{},
|
||||
/*.pad0[2]=*/{},
|
||||
};
|
||||
|
||||
// Querying the root window's pointer gives its absolute value
|
||||
const xcb_query_pointer_reply_t rootWindowQueryPointerReply{
|
||||
/*.response_type=*/XCB_QUERY_POINTER,
|
||||
/*.same_screen=*/1,
|
||||
/*.sequence=*/0,
|
||||
/*.length=*/1,
|
||||
/*.root=*/s_rootWindow,
|
||||
/*.child=*/s_rootWindow,
|
||||
/*.root_x=*/static_cast<int16_t>(GetParam().m_x + s_defaultWindowGeometry.x),
|
||||
/*.root_y=*/static_cast<int16_t>(GetParam().m_y + s_defaultWindowGeometry.y),
|
||||
/*.win_x=*/static_cast<int16_t>(GetParam().m_x + s_defaultWindowGeometry.x),
|
||||
/*.win_y=*/static_cast<int16_t>(GetParam().m_y + s_defaultWindowGeometry.y),
|
||||
/*.mask=*/{},
|
||||
/*.pad0[2]=*/{},
|
||||
};
|
||||
|
||||
EXPECT_CALL(m_interface, xcb_get_property_value(Field(&xcb_get_property_reply_t::sequence, Eq(s_getActiveWindowPropertySequence))))
|
||||
.WillRepeatedly(Return(&focusWindow));
|
||||
|
||||
EXPECT_CALL(m_interface, xcb_query_pointer(&m_connection, focusWindow))
|
||||
.WillRepeatedly(Return(xcb_query_pointer_cookie_t{/*.sequence=*/1}));
|
||||
EXPECT_CALL(m_interface, xcb_query_pointer_reply(&m_connection, Field(&xcb_query_pointer_cookie_t::sequence, 1), _))
|
||||
.WillRepeatedly(ReturnMalloc<xcb_query_pointer_reply_t>(queryPointerReply));
|
||||
|
||||
EXPECT_CALL(m_interface, xcb_query_pointer(&m_connection, s_rootWindow))
|
||||
.WillRepeatedly(Return(xcb_query_pointer_cookie_t{/*.sequence=*/2}));
|
||||
EXPECT_CALL(m_interface, xcb_query_pointer_reply(&m_connection, Field(&xcb_query_pointer_cookie_t::sequence, 2), _))
|
||||
.WillRepeatedly(ReturnMalloc<xcb_query_pointer_reply_t>(rootWindowQueryPointerReply));
|
||||
|
||||
m_application.Start();
|
||||
InputSystemCursorRequestBus::Event(
|
||||
InputDeviceMouse::Id,
|
||||
&InputSystemCursorRequests::SetSystemCursorState,
|
||||
SystemCursorState::ConstrainedAndHidden);
|
||||
|
||||
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
|
||||
InputSystemCursorRequestBus::EventResult(
|
||||
systemCursorPositionNormalized,
|
||||
InputDeviceMouse::Id,
|
||||
&InputSystemCursorRequests::GetSystemCursorPositionNormalized);
|
||||
|
||||
EXPECT_THAT(systemCursorPositionNormalized, ::testing::AllOf(
|
||||
testing::Property(&AZ::Vector2::GetX, testing::FloatEq(static_cast<float>(GetParam().m_x) / s_defaultWindowGeometry.width)),
|
||||
testing::Property(&AZ::Vector2::GetY, testing::FloatEq(static_cast<float>(GetParam().m_y) / s_defaultWindowGeometry.height))
|
||||
));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
AllPointerPositions,
|
||||
XcbGetSystemCursorPositionTests,
|
||||
testing::Values(
|
||||
// Default mocked window geometry sets width and height to 100, all
|
||||
// parameter values should be within [0, 100)
|
||||
GetCursorPositionParam{ 50, 50 },
|
||||
GetCursorPositionParam{ 25, 25 },
|
||||
GetCursorPositionParam{ 0, 100 }
|
||||
)
|
||||
);
|
||||
} // namespace AzFramework
|
||||
@@ -17,5 +17,6 @@ set(FILES
|
||||
XcbBaseTestFixture.cpp
|
||||
XcbBaseTestFixture.h
|
||||
XcbInputDeviceKeyboardTests.cpp
|
||||
XcbInputDeviceMouseTests.cpp
|
||||
XcbTestApplication.h
|
||||
)
|
||||
|
||||
+2
-2
@@ -40,9 +40,9 @@ namespace AzToolsFramework
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
QModelIndex parent(const QModelIndex& child) const override;
|
||||
QModelIndex sibling(int row, int column, const QModelIndex& idx) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
||||
protected:
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role /* = Qt::DisplayRole */) const override;
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace AzToolsFramework
|
||||
private slots:
|
||||
void SourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
||||
private:
|
||||
AZ::u64 m_numberOfItemsDisplayed = 0;
|
||||
AZ::u64 m_numberOfItemsDisplayed = 50;
|
||||
int m_displayedItemsCounter = 0;
|
||||
QPointer<AssetBrowserFilterModel> m_filterModel;
|
||||
QMap<int, QModelIndex> m_indexMap;
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* 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 <AzTest/AzTest.h>
|
||||
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserComponent.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserFilterModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/AssetBrowserTableModel.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/AssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/FolderAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/ProductAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/RootAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Entries/SourceAssetBrowserEntry.h>
|
||||
#include <AzToolsFramework/AssetBrowser/Search/SearchWidget.h>
|
||||
#include <AzToolsFramework/AssetDatabase/AssetDatabaseConnection.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityContextComponent.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <QAbstractItemModelTester>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
// Test fixture for the AssetBrowser model that uses a QAbstractItemModelTester to validate the state of the model
|
||||
// when QAbstractItemModel signals fire. Tests will exit with a fatal error if an invalid state is detected.
|
||||
class AssetBrowserTest
|
||||
: public ToolsApplicationFixture
|
||||
, public testing::WithParamInterface<const char*>
|
||||
{
|
||||
protected:
|
||||
enum class AssetEntryType
|
||||
{
|
||||
Root,
|
||||
Folder,
|
||||
Source,
|
||||
Product
|
||||
};
|
||||
|
||||
enum class FolderType
|
||||
{
|
||||
Root,
|
||||
File
|
||||
};
|
||||
|
||||
void SetUpEditorFixtureImpl() override;
|
||||
void TearDownEditorFixtureImpl() override;
|
||||
|
||||
//! Creates a Mock Scan Folder
|
||||
void AddScanFolder(AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType = FolderType::File);
|
||||
|
||||
//! Creates a Source entry from a mock file
|
||||
AZ::Uuid CreateSourceEntry(
|
||||
AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType = AssetEntryType::Source);
|
||||
|
||||
//! Creates a product from a given sourceEntry
|
||||
void CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName);
|
||||
|
||||
void SetupAssetBrowser();
|
||||
void PrintModel(const QAbstractItemModel* model, AZStd::function<void(const QString&)> printer);
|
||||
QModelIndex GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row = 0);
|
||||
AZStd::shared_ptr<AzToolsFramework::AssetBrowser::RootAssetBrowserEntry> GetRootEntry();
|
||||
AZStd::vector<QString> GetVectorFromFormattedString(const QString& formattedString);
|
||||
|
||||
protected:
|
||||
QString m_assetBrowserHierarchy = QString();
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::SearchWidget> m_searchWidget;
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserComponent> m_assetBrowserComponent;
|
||||
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserFilterModel> m_filterModel;
|
||||
AZStd::unique_ptr<AzToolsFramework::AssetBrowser::AssetBrowserTableModel> m_tableModel;
|
||||
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterAssetBrowser;
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterFilterModel;
|
||||
AZStd::unique_ptr<QAbstractItemModelTester> m_modelTesterTableModel;
|
||||
|
||||
QVector<int> m_folderIds = { 13, 14, 15 };
|
||||
QVector<int> m_sourceIDs = { 1, 2, 3, 4, 5 };
|
||||
QVector<int> m_productIDs = { 1, 2, 3, 4, 5 };
|
||||
};
|
||||
|
||||
void AssetBrowserTest::SetUpEditorFixtureImpl()
|
||||
{
|
||||
GetApplication()->RegisterComponentDescriptor(AzToolsFramework::EditorEntityContextComponent::CreateDescriptor());
|
||||
|
||||
m_assetBrowserComponent = AZStd::make_unique<AzToolsFramework::AssetBrowser::AssetBrowserComponent>();
|
||||
m_assetBrowserComponent->Activate();
|
||||
|
||||
m_filterModel = AZStd::make_unique<AzToolsFramework::AssetBrowser::AssetBrowserFilterModel>();
|
||||
m_tableModel = AZStd::make_unique<AzToolsFramework::AssetBrowser::AssetBrowserTableModel>();
|
||||
|
||||
m_filterModel->setSourceModel(m_assetBrowserComponent->GetAssetBrowserModel());
|
||||
m_tableModel->setSourceModel(m_filterModel.get());
|
||||
|
||||
m_modelTesterAssetBrowser = AZStd::make_unique<QAbstractItemModelTester>(m_assetBrowserComponent->GetAssetBrowserModel());
|
||||
m_modelTesterFilterModel = AZStd::make_unique<QAbstractItemModelTester>(m_filterModel.get());
|
||||
m_modelTesterTableModel = AZStd::make_unique<QAbstractItemModelTester>(m_tableModel.get());
|
||||
m_searchWidget = AZStd::make_unique<AzToolsFramework::AssetBrowser::SearchWidget>();
|
||||
|
||||
// Setup String filters
|
||||
m_searchWidget->Setup(true, true);
|
||||
m_filterModel->SetFilter(m_searchWidget->GetFilter());
|
||||
|
||||
SetupAssetBrowser();
|
||||
}
|
||||
|
||||
void AssetBrowserTest::TearDownEditorFixtureImpl()
|
||||
{
|
||||
m_modelTesterAssetBrowser.reset();
|
||||
m_modelTesterFilterModel.reset();
|
||||
m_modelTesterTableModel.reset();
|
||||
|
||||
m_tableModel.reset();
|
||||
m_filterModel.reset();
|
||||
m_assetBrowserComponent->Deactivate();
|
||||
|
||||
m_assetBrowserComponent.reset();
|
||||
m_searchWidget.reset();
|
||||
}
|
||||
|
||||
void AssetBrowserTest::AddScanFolder(
|
||||
AZ::s64 folderID, AZStd::string folderPath, AZStd::string displayName, FolderType folderType /*= FolderType::File*/)
|
||||
{
|
||||
AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry scanFolder = AzToolsFramework::AssetDatabase::ScanFolderDatabaseEntry();
|
||||
scanFolder.m_scanFolderID = folderID;
|
||||
scanFolder.m_scanFolder = folderPath;
|
||||
scanFolder.m_displayName = displayName;
|
||||
scanFolder.m_isRoot = folderType == FolderType::Root;
|
||||
GetRootEntry()->AddScanFolder(scanFolder);
|
||||
}
|
||||
|
||||
AZ::Uuid AssetBrowserTest::CreateSourceEntry(
|
||||
AZ::s64 fileID, AZ::s64 parentFolderID, AZStd::string filename, AssetEntryType sourceType /*= AssetEntryType::Source*/)
|
||||
{
|
||||
AzToolsFramework::AssetDatabase::FileDatabaseEntry entry = AzToolsFramework::AssetDatabase::FileDatabaseEntry();
|
||||
entry.m_scanFolderPK = parentFolderID;
|
||||
entry.m_fileID = fileID;
|
||||
entry.m_fileName = filename;
|
||||
entry.m_isFolder = sourceType == AssetEntryType::Folder;
|
||||
GetRootEntry()->AddFile(entry);
|
||||
|
||||
if (!entry.m_isFolder)
|
||||
{
|
||||
AzToolsFramework::AssetBrowser::SourceWithFileID entrySource = AzToolsFramework::AssetBrowser::SourceWithFileID();
|
||||
entrySource.first = entry.m_fileID;
|
||||
entrySource.second = AzToolsFramework::AssetDatabase::SourceDatabaseEntry();
|
||||
entrySource.second.m_scanFolderPK = parentFolderID;
|
||||
entrySource.second.m_sourceName = filename;
|
||||
entrySource.second.m_sourceID = fileID;
|
||||
entrySource.second.m_sourceGuid = AZ::Uuid::CreateRandom();
|
||||
|
||||
GetRootEntry()->AddSource(entrySource);
|
||||
|
||||
return entrySource.second.m_sourceGuid;
|
||||
}
|
||||
|
||||
return AZ::Uuid::CreateNull();
|
||||
}
|
||||
|
||||
void AssetBrowserTest::CreateProduct(AZ::s64 productID, AZ::Uuid sourceUuid, AZStd::string productName)
|
||||
{
|
||||
AzToolsFramework::AssetBrowser::ProductWithUuid product = AzToolsFramework::AssetBrowser::ProductWithUuid();
|
||||
product.first = sourceUuid;
|
||||
product.second = AzToolsFramework::AssetDatabase::ProductDatabaseEntry();
|
||||
product.second.m_productID = productID;
|
||||
|
||||
product.second.m_subID = aznumeric_cast<AZ::u32>(productID);
|
||||
product.second.m_productName = productName;
|
||||
|
||||
GetRootEntry()->AddProduct(product);
|
||||
}
|
||||
|
||||
void AssetBrowserTest::SetupAssetBrowser()
|
||||
{
|
||||
// RootEntries : 1 | Folders : 4 | SourceEntries : 5 | ProductEntries : 9
|
||||
m_assetBrowserHierarchy = R"(
|
||||
D:
|
||||
\
|
||||
dev
|
||||
o3de
|
||||
GameProject
|
||||
Assets
|
||||
Source_1
|
||||
Product_1_1
|
||||
Product_1_0
|
||||
Source_0
|
||||
Product_0_3
|
||||
Product_0_2
|
||||
Product_0_1
|
||||
Product_0_0
|
||||
Scripts
|
||||
Source_3
|
||||
Source_2
|
||||
Product_2_2
|
||||
Product_2_1
|
||||
Product_2_0
|
||||
Misc
|
||||
Source_4
|
||||
Product_4_2
|
||||
Product_4_1
|
||||
Product_4_0 )";
|
||||
|
||||
namespace AzAssetBrowser = AzToolsFramework::AssetBrowser;
|
||||
|
||||
AddScanFolder(m_folderIds.at(2), "D:/dev/o3de/GameProject/Misc", "Misc");
|
||||
AZ::Uuid sourceUuid_4 = CreateSourceEntry(m_sourceIDs.at(4), m_folderIds.at(2), "Source_4");
|
||||
CreateProduct(m_productIDs.at(0), sourceUuid_4, "Product_4_0");
|
||||
CreateProduct(m_productIDs.at(1), sourceUuid_4, "Product_4_1");
|
||||
CreateProduct(m_productIDs.at(2), sourceUuid_4, "Product_4_2");
|
||||
|
||||
AddScanFolder(m_folderIds.at(1), "D:/dev/o3de/GameProject/Scripts", "Scripts");
|
||||
|
||||
AZ::Uuid sourceUuid_2 = CreateSourceEntry(m_sourceIDs.at(2), m_folderIds.at(1), "Source_2");
|
||||
CreateProduct(m_productIDs.at(0), sourceUuid_2, "Product_2_0");
|
||||
CreateProduct(m_productIDs.at(1), sourceUuid_2, "Product_2_1");
|
||||
CreateProduct(m_productIDs.at(2), sourceUuid_2, "Product_2_2");
|
||||
|
||||
CreateSourceEntry(m_sourceIDs.at(3), m_folderIds.at(1), "Source_3");
|
||||
|
||||
AddScanFolder(m_folderIds.at(0), "D:/dev/o3de/GameProject/Assets", "Assets");
|
||||
|
||||
AZ::Uuid sourceUuid_0 = CreateSourceEntry(m_sourceIDs.at(0), m_folderIds.at(0), "Source_0");
|
||||
CreateProduct(m_productIDs.at(0), sourceUuid_0, "Product_0_0");
|
||||
CreateProduct(m_productIDs.at(1), sourceUuid_0, "Product_0_1");
|
||||
CreateProduct(m_productIDs.at(2), sourceUuid_0, "Product_0_2");
|
||||
CreateProduct(m_productIDs.at(3), sourceUuid_0, "Product_0_3");
|
||||
|
||||
AZ::Uuid sourceUuid_1 = CreateSourceEntry(m_sourceIDs.at(1), m_folderIds.at(0), "Source_1");
|
||||
CreateProduct(m_productIDs.at(0), sourceUuid_1, "Product_1_0");
|
||||
CreateProduct(m_productIDs.at(1), sourceUuid_1, "Product_1_1");
|
||||
}
|
||||
|
||||
void AssetBrowserTest::PrintModel(const QAbstractItemModel* model, AZStd::function<void(const QString&)> printer)
|
||||
{
|
||||
AZStd::deque<AZStd::pair<QModelIndex, int>> indices;
|
||||
indices.push_back({ model->index(0, 0), 0 });
|
||||
while (!indices.empty())
|
||||
{
|
||||
auto [index, depth] = indices.front();
|
||||
indices.pop_front();
|
||||
|
||||
QString indentString;
|
||||
for (int i = 0; i < depth; ++i)
|
||||
{
|
||||
indentString += " ";
|
||||
}
|
||||
const QString message = indentString + index.data(Qt::DisplayRole).toString();
|
||||
printer(message);
|
||||
|
||||
for (int i = 0; i < model->rowCount(index); ++i)
|
||||
{
|
||||
indices.emplace_front(model->index(i, 0, index), depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex AssetBrowserTest::GetModelIndex(const QAbstractItemModel* model, int targetDepth, int row)
|
||||
{
|
||||
AZStd::deque<AZStd::pair<QModelIndex, int>> indices;
|
||||
indices.push_back({ model->index(0, 0), 0 });
|
||||
while (!indices.empty())
|
||||
{
|
||||
auto [index, depth] = indices.front();
|
||||
indices.pop_front();
|
||||
|
||||
for (int i = 0; i < model->rowCount(index); ++i)
|
||||
{
|
||||
if (depth + 1 == targetDepth && row == i)
|
||||
{
|
||||
return model->index(i, 0, index);
|
||||
}
|
||||
indices.emplace_front(model->index(i, 0, index), depth + 1);
|
||||
}
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
AZStd::shared_ptr<AzToolsFramework::AssetBrowser::RootAssetBrowserEntry> AssetBrowserTest::GetRootEntry()
|
||||
{
|
||||
return m_assetBrowserComponent->GetAssetBrowserModel()->GetRootEntry();
|
||||
}
|
||||
|
||||
AZStd::vector<QString> AssetBrowserTest::GetVectorFromFormattedString(const QString& formattedString)
|
||||
{
|
||||
AZStd::vector<QString> hierarchySections;
|
||||
QStringList splittedList = formattedString.split('\n', Qt::SkipEmptyParts);
|
||||
|
||||
for (auto& str : splittedList)
|
||||
{
|
||||
str.replace(" ", "");
|
||||
hierarchySections.push_back(str);
|
||||
}
|
||||
return hierarchySections;
|
||||
}
|
||||
|
||||
TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableView)
|
||||
{
|
||||
m_filterModel->FilterUpdatedSlotImmediate();
|
||||
const int tableViewRowcount = m_tableModel->rowCount();
|
||||
|
||||
// RowCount should be 17 -> 5 SourceEntries + 12 ProductEntries)
|
||||
EXPECT_EQ(tableViewRowcount, 17);
|
||||
}
|
||||
|
||||
TEST_F(AssetBrowserTest, CheckCorrectNumberOfEntriesInTableViewAfterStringFilter)
|
||||
{
|
||||
/*
|
||||
*-Source_1
|
||||
* |
|
||||
* |-product_1_0
|
||||
* |-product_1_1
|
||||
*
|
||||
*
|
||||
* Matching entries = 3
|
||||
*/
|
||||
|
||||
// Apply string filter
|
||||
m_searchWidget->SetTextFilter(QString("source_1"));
|
||||
m_filterModel->FilterUpdatedSlotImmediate();
|
||||
|
||||
const int tableViewRowcount = m_tableModel->rowCount();
|
||||
EXPECT_EQ(tableViewRowcount, 3);
|
||||
}
|
||||
|
||||
TEST_F(AssetBrowserTest, CheckScanFolderAddition)
|
||||
{
|
||||
EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1);
|
||||
const int newFolderId = 20;
|
||||
AddScanFolder(newFolderId, "E:/TestFolder/TestFolder2", "TestFolder");
|
||||
|
||||
// Since the folder is empty it shouldn't be added to the model.
|
||||
EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 1);
|
||||
|
||||
CreateSourceEntry(123, newFolderId, "DummyFile");
|
||||
|
||||
// When we add a file to the folder it should be added to the model
|
||||
EXPECT_EQ(m_assetBrowserComponent->GetAssetBrowserModel()->rowCount(), 2);
|
||||
}
|
||||
|
||||
} // namespace UnitTest
|
||||
@@ -123,6 +123,7 @@ set(FILES
|
||||
UI/EntityIdQLineEditTests.cpp
|
||||
UI/EntityOutlinerTests.cpp
|
||||
UI/EntityPropertyEditorTests.cpp
|
||||
UI/AssetBrowserTests.cpp
|
||||
UndoStack.cpp
|
||||
Viewport/ClusterTests.cpp
|
||||
Viewport/ViewportEditorModeTests.cpp
|
||||
|
||||
@@ -76,6 +76,12 @@ foreach(project_name project_path IN ZIP_LISTS LY_PROJECTS_TARGET_NAME LY_PROJEC
|
||||
Legacy::CrySystem
|
||||
)
|
||||
|
||||
if(PAL_TRAIT_BUILD_SERVER_SUPPORTED)
|
||||
set(server_runtime_dependencies
|
||||
Legacy::CrySystem
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -1121,8 +1121,8 @@ inline ISystem* GetISystem()
|
||||
|
||||
// Description:
|
||||
// This function must be called once by each module at the beginning, to setup global pointers.
|
||||
extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, const char* moduleName);
|
||||
extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem(ISystem* pSystem);
|
||||
void ModuleInitISystem(ISystem* pSystem, const char* moduleName);
|
||||
void ModuleShutdownISystem(ISystem* pSystem);
|
||||
extern "C" AZ_DLL_EXPORT void InjectEnvironment(void* env);
|
||||
extern "C" AZ_DLL_EXPORT void DetachEnvironment();
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ void InitCRTHandlers() {}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// This is an entry to DLL initialization function that must be called for each loaded module
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName)
|
||||
void ModuleInitISystem(ISystem* pSystem, [[maybe_unused]] const char* moduleName)
|
||||
{
|
||||
if (gEnv) // Already registered.
|
||||
{
|
||||
@@ -96,7 +96,7 @@ extern "C" AZ_DLL_EXPORT void ModuleInitISystem(ISystem* pSystem, [[maybe_unused
|
||||
} // if pSystem
|
||||
}
|
||||
|
||||
extern "C" AZ_DLL_EXPORT void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem)
|
||||
void ModuleShutdownISystem([[maybe_unused]] ISystem* pSystem)
|
||||
{
|
||||
// Unregister with AZ environment.
|
||||
AZ::Environment::Detach();
|
||||
|
||||
@@ -173,8 +173,6 @@ void CryEngineSignalHandler(int signal)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#if defined(WIN32) || defined(LINUX) || defined(APPLE)
|
||||
# define DLL_MODULE_INIT_ISYSTEM "ModuleInitISystem"
|
||||
# define DLL_MODULE_SHUTDOWN_ISYSTEM "ModuleShutdownISystem"
|
||||
# define DLL_INITFUNC_RENDERER "PackageRenderConstructor"
|
||||
# define DLL_INITFUNC_SOUND "CreateSoundSystem"
|
||||
# define DLL_INITFUNC_FONT "CreateCryFontInterface"
|
||||
@@ -188,8 +186,6 @@ void CryEngineSignalHandler(int signal)
|
||||
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
||||
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
||||
#else
|
||||
# define DLL_MODULE_INIT_ISYSTEM (LPCSTR)2
|
||||
# define DLL_MODULE_SHUTDOWN_ISYSTEM (LPCSTR)3
|
||||
# define DLL_INITFUNC_RENDERER (LPCSTR)1
|
||||
# define DLL_INITFUNC_RENDERER (LPCSTR)1
|
||||
# define DLL_INITFUNC_SOUND (LPCSTR)1
|
||||
@@ -445,18 +441,6 @@ AZStd::unique_ptr<AZ::DynamicModuleHandle> CSystem::LoadDLL(const char* dllName)
|
||||
return handle;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// After loading DLL initialize it by calling ModuleInitISystem
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
AZStd::string moduleName = PathUtil::GetFileName(dllName);
|
||||
|
||||
typedef void*(*PtrFunc_ModuleInitISystem)(ISystem* pSystem, const char* moduleName);
|
||||
PtrFunc_ModuleInitISystem pfnModuleInitISystem = handle->GetFunction<PtrFunc_ModuleInitISystem>(DLL_MODULE_INIT_ISYSTEM);
|
||||
if (pfnModuleInitISystem)
|
||||
{
|
||||
pfnModuleInitISystem(this, moduleName.c_str());
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -497,13 +481,6 @@ void CSystem::ShutdownModuleLibraries()
|
||||
#if !defined(AZ_MONOLITHIC_BUILD)
|
||||
for (auto iterator = m_moduleDLLHandles.begin(); iterator != m_moduleDLLHandles.end(); ++iterator)
|
||||
{
|
||||
typedef void*( * PtrFunc_ModuleShutdownISystem )(ISystem* pSystem);
|
||||
|
||||
PtrFunc_ModuleShutdownISystem pfnModuleShutdownISystem = iterator->second->GetFunction<PtrFunc_ModuleShutdownISystem>(DLL_MODULE_SHUTDOWN_ISYSTEM);
|
||||
if (pfnModuleShutdownISystem)
|
||||
{
|
||||
pfnModuleShutdownISystem(this);
|
||||
}
|
||||
if (iterator->second->IsLoaded())
|
||||
{
|
||||
iterator->second->Unload();
|
||||
|
||||
@@ -333,11 +333,6 @@ void CXConsole::Init(ISystem* pSystem)
|
||||
|
||||
m_nLoadingBackTexID = -1;
|
||||
|
||||
if (gEnv->IsDedicated())
|
||||
{
|
||||
m_bConsoleActive = true;
|
||||
}
|
||||
|
||||
REGISTER_COMMAND("ConsoleShow", &ConsoleShow, VF_NULL, "Opens the console");
|
||||
REGISTER_COMMAND("ConsoleHide", &ConsoleHide, VF_NULL, "Closes the console");
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace AssetProcessor
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
UnitTestUtils::AssertAbsorber* m_errorAbsorber;
|
||||
AZStd::unique_ptr<UnitTestUtils::AssertAbsorber> m_errorAbsorber{};
|
||||
FileStatePassthrough m_fileStateCache;
|
||||
|
||||
void SetUp() override
|
||||
@@ -40,7 +40,7 @@ namespace AssetProcessor
|
||||
m_ownsSysAllocator = true;
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
}
|
||||
m_errorAbsorber = new UnitTestUtils::AssertAbsorber();
|
||||
m_errorAbsorber = AZStd::make_unique<UnitTestUtils::AssertAbsorber>();
|
||||
|
||||
m_application = AZStd::make_unique<AzFramework::Application>();
|
||||
|
||||
@@ -62,8 +62,8 @@ namespace AssetProcessor
|
||||
AssetUtilities::ResetAssetRoot();
|
||||
|
||||
m_application.reset();
|
||||
delete m_errorAbsorber;
|
||||
m_errorAbsorber = nullptr;
|
||||
m_errorAbsorber.reset();
|
||||
|
||||
if (m_ownsSysAllocator)
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
|
||||
|
||||
@@ -4139,11 +4139,19 @@ struct LockedFileTest
|
||||
switch (message.GetMessageType())
|
||||
{
|
||||
case SourceFileNotificationMessage::MessageType:
|
||||
if (const auto sourceFileMessage = azrtti_cast<const SourceFileNotificationMessage*>(&message);
|
||||
sourceFileMessage != nullptr && sourceFileMessage->m_type == SourceFileNotificationMessage::NotificationType::FileRemoved
|
||||
&& m_callback)
|
||||
if (const auto sourceFileMessage = azrtti_cast<const SourceFileNotificationMessage*>(&message); sourceFileMessage != nullptr &&
|
||||
sourceFileMessage->m_type == SourceFileNotificationMessage::NotificationType::FileRemoved)
|
||||
{
|
||||
m_callback();
|
||||
// The File Remove message will occur before an attempt to delete the file
|
||||
// Wait for more than 1 File Remove message.
|
||||
// This indicates the AP has attempted to delete the file once, failed to do so and is now retrying
|
||||
++m_deleteCounter;
|
||||
|
||||
if(m_deleteCounter > 1 && m_callback)
|
||||
{
|
||||
m_callback();
|
||||
m_callback = {}; // Unset it to be safe, we only intend to run the callback once
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -4167,6 +4175,7 @@ struct LockedFileTest
|
||||
ModtimeScanningTest::TearDown();
|
||||
}
|
||||
|
||||
AZStd::atomic_int m_deleteCounter{ 0 };
|
||||
AZStd::function<void()> m_callback;
|
||||
};
|
||||
|
||||
@@ -4206,6 +4215,10 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeleteFails)
|
||||
|
||||
TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased)
|
||||
{
|
||||
// This test is intended to verify the AP will successfully retry deleting a source asset
|
||||
// when one of its product assets is locked temporarily
|
||||
// We'll lock the file by holding it open
|
||||
|
||||
auto theFile = m_data->m_absolutePath[1].toUtf8();
|
||||
const char* theFileString = theFile.constData();
|
||||
auto [sourcePath, productPath] = *m_data->m_productPaths.find(theFileString);
|
||||
@@ -4218,19 +4231,22 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased)
|
||||
ASSERT_GT(m_data->m_productPaths.size(), 0);
|
||||
QFile product(productPath);
|
||||
|
||||
// Open the file and keep it open to lock it
|
||||
// We'll start a thread later to unlock the file
|
||||
// This will allow us to test how AP handles trying to delete a locked file
|
||||
ASSERT_TRUE(product.open(QIODevice::ReadOnly));
|
||||
|
||||
// Check if we can delete the file now, if we can't, proceed with the test
|
||||
// If we can, it means the OS running this test doesn't lock open files so there's nothing to test
|
||||
if (!AZ::IO::SystemFile::Delete(productPath.toUtf8().constData()))
|
||||
{
|
||||
AZStd::thread workerThread;
|
||||
m_deleteCounter = 0;
|
||||
|
||||
m_callback = [&product, &workerThread]() {
|
||||
workerThread = AZStd::thread([&product]() {
|
||||
AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(60));
|
||||
product.close();
|
||||
});
|
||||
// Set up a callback which will fire after at least 1 retry
|
||||
// Unlock the file at that point so AP can successfully delete it
|
||||
m_callback = [&product]()
|
||||
{
|
||||
product.close();
|
||||
};
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
@@ -4240,8 +4256,9 @@ TEST_F(LockedFileTest, DeleteFile_LockedProduct_DeletesWhenReleased)
|
||||
|
||||
EXPECT_FALSE(QFile::exists(productPath));
|
||||
EXPECT_EQ(m_data->m_deletedSources.size(), 1);
|
||||
|
||||
workerThread.join();
|
||||
|
||||
EXPECT_GT(m_deleteCounter, 1); // Make sure the AP tried more than once to delete the file
|
||||
m_errorAbsorber->ExpectAsserts(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -49,6 +49,12 @@ int main(int argc, char* argv[])
|
||||
AZStd::unique_ptr<AzFramework::ProcessWatcher> shellProcess(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE));
|
||||
shellProcess->WaitForProcessToExit(120);
|
||||
shellProcess.reset();
|
||||
|
||||
parameters = AZStd::string::format("-c \"%s/scripts/o3de.sh register --this-engine\"", enginePath.c_str());
|
||||
shellProcessLaunch.m_commandlineParameters = parameters;
|
||||
shellProcess.reset(AzFramework::ProcessWatcher::LaunchProcess(shellProcessLaunch, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE));
|
||||
shellProcess->WaitForProcessToExit(120);
|
||||
shellProcess.reset();
|
||||
|
||||
AZ::IO::FixedMaxPath projectManagerPath = installedBinariesFolder/"o3de.app"/"Contents"/"MacOS"/"o3de";
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <QProcessEnvironment>
|
||||
#include <QDir>
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
@@ -94,5 +96,10 @@ namespace O3DE::ProjectManager
|
||||
QProcessEnvironment::systemEnvironment(),
|
||||
QObject::tr("Running get_python script..."));
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath GetEditorDirectory()
|
||||
{
|
||||
return AZ::Utils::GetExecutableDirectory();
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
@@ -104,5 +107,35 @@ namespace O3DE::ProjectManager
|
||||
QProcessEnvironment::systemEnvironment(),
|
||||
QObject::tr("Running get_python script..."));
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath GetEditorDirectory()
|
||||
{
|
||||
AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory();
|
||||
AZ::IO::FixedMaxPath editorPath{ executableDirectory };
|
||||
editorPath /= "../../../Editor.app/Contents/MacOS";
|
||||
editorPath = editorPath.LexicallyNormal();
|
||||
if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str()))
|
||||
{
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath installedBinariesPath;
|
||||
settingsRegistry->Get(installedBinariesPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
|
||||
{
|
||||
if (AZ::IO::FixedMaxPath engineRootFolder;
|
||||
settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
|
||||
{
|
||||
editorPath = engineRootFolder / installedBinariesPath / "Editor.app/Contents/MacOS";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!AZ::IO::SystemFile::IsDirectory(editorPath.c_str()))
|
||||
{
|
||||
AZ_Error("ProjectManager", false, "Unable to find the Editor app bundle!");
|
||||
}
|
||||
}
|
||||
|
||||
return editorPath;
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
{
|
||||
namespace ProjectUtils
|
||||
@@ -139,5 +141,10 @@ namespace O3DE::ProjectManager
|
||||
QProcessEnvironment::systemEnvironment(),
|
||||
QObject::tr("Running get_python script..."));
|
||||
}
|
||||
|
||||
AZ::IO::FixedMaxPath GetEditorDirectory()
|
||||
{
|
||||
return AZ::Utils::GetExecutableDirectory();
|
||||
}
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -81,6 +81,8 @@ namespace O3DE::ProjectManager
|
||||
DownloadStatus m_downloadStatus = UnknownDownloadStatus;
|
||||
QStringList m_features;
|
||||
QString m_requirement;
|
||||
QString m_licenseText;
|
||||
QString m_licenseLink;
|
||||
QString m_directoryLink;
|
||||
QString m_documentationLink;
|
||||
QString m_version = "Unknown Version";
|
||||
|
||||
@@ -52,6 +52,22 @@ namespace O3DE::ProjectManager
|
||||
Update(selectedIndices[0]);
|
||||
}
|
||||
|
||||
void SetLabelElidedText(QLabel* label, QString text)
|
||||
{
|
||||
QFontMetrics nameFontMetrics(label->font());
|
||||
int labelWidth = label->width();
|
||||
|
||||
// Don't elide if the widgets are sized too small (sometimes occurs when loading gem catalog)
|
||||
if (labelWidth > 100)
|
||||
{
|
||||
label->setText(nameFontMetrics.elidedText(text, Qt::ElideRight, labelWidth));
|
||||
}
|
||||
else
|
||||
{
|
||||
label->setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
void GemInspector::Update(const QModelIndex& modelIndex)
|
||||
{
|
||||
if (!modelIndex.isValid())
|
||||
@@ -59,38 +75,52 @@ namespace O3DE::ProjectManager
|
||||
m_mainWidget->hide();
|
||||
}
|
||||
|
||||
m_nameLabel->setText(m_model->GetDisplayName(modelIndex));
|
||||
m_creatorLabel->setText(m_model->GetCreator(modelIndex));
|
||||
SetLabelElidedText(m_nameLabel, m_model->GetDisplayName(modelIndex));
|
||||
SetLabelElidedText(m_creatorLabel, m_model->GetCreator(modelIndex));
|
||||
|
||||
m_summaryLabel->setText(m_model->GetSummary(modelIndex));
|
||||
m_summaryLabel->adjustSize();
|
||||
|
||||
m_licenseLinkLabel->setText(m_model->GetLicenseText(modelIndex));
|
||||
m_licenseLinkLabel->SetUrl(m_model->GetLicenseLink(modelIndex));
|
||||
|
||||
m_directoryLinkLabel->SetUrl(m_model->GetDirectoryLink(modelIndex));
|
||||
m_documentationLinkLabel->SetUrl(m_model->GetDocLink(modelIndex));
|
||||
|
||||
if (m_model->HasRequirement(modelIndex))
|
||||
{
|
||||
m_reqirementsIconLabel->show();
|
||||
m_reqirementsTitleLabel->show();
|
||||
m_reqirementsTextLabel->show();
|
||||
m_requirementsIconLabel->show();
|
||||
m_requirementsTitleLabel->show();
|
||||
m_requirementsTextLabel->show();
|
||||
m_requirementsMainSpacer->changeSize(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
m_reqirementsTitleLabel->setText("Requirement");
|
||||
m_reqirementsTextLabel->setText(m_model->GetRequirement(modelIndex));
|
||||
m_requirementsTitleLabel->setText(tr("Requirement"));
|
||||
m_requirementsTextLabel->setText(m_model->GetRequirement(modelIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reqirementsIconLabel->hide();
|
||||
m_reqirementsTitleLabel->hide();
|
||||
m_reqirementsTextLabel->hide();
|
||||
m_requirementsIconLabel->hide();
|
||||
m_requirementsTitleLabel->hide();
|
||||
m_requirementsTextLabel->hide();
|
||||
m_requirementsMainSpacer->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
// Depending gems
|
||||
m_dependingGems->Update("Depending Gems", "The following Gems will be automatically enabled with this Gem.", m_model->GetDependingGemNames(modelIndex));
|
||||
QStringList dependingGems = m_model->GetDependingGemNames(modelIndex);
|
||||
if (!dependingGems.isEmpty())
|
||||
{
|
||||
m_dependingGems->Update(tr("Depending Gems"), tr("The following Gems will be automatically enabled with this Gem."), dependingGems);
|
||||
m_dependingGems->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dependingGems->hide();
|
||||
}
|
||||
|
||||
// Additional information
|
||||
m_versionLabel->setText(QString("Gem Version: %1").arg(m_model->GetVersion(modelIndex)));
|
||||
m_lastUpdatedLabel->setText(QString("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex)));
|
||||
m_binarySizeLabel->setText(QString("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex)));
|
||||
m_versionLabel->setText(tr("Gem Version: %1").arg(m_model->GetVersion(modelIndex)));
|
||||
m_lastUpdatedLabel->setText(tr("Last Updated: %1").arg(m_model->GetLastUpdated(modelIndex)));
|
||||
m_binarySizeLabel->setText(tr("Binary Size: %1 KB").arg(m_model->GetBinarySizeInKB(modelIndex)));
|
||||
|
||||
m_mainWidget->adjustSize();
|
||||
m_mainWidget->show();
|
||||
@@ -108,35 +138,51 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
// Gem name, creator and summary
|
||||
m_nameLabel = CreateStyledLabel(m_mainLayout, 18, s_headerColor);
|
||||
m_creatorLabel = CreateStyledLabel(m_mainLayout, 12, s_headerColor);
|
||||
m_creatorLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_headerColor);
|
||||
m_mainLayout->addSpacing(5);
|
||||
|
||||
// TODO: QLabel seems to have issues determining the right sizeHint() for our font with the given font size.
|
||||
// This results into squeezed elements in the layout in case the text is a little longer than a sentence.
|
||||
m_summaryLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
|
||||
m_summaryLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_headerColor);
|
||||
m_mainLayout->addWidget(m_summaryLabel);
|
||||
m_summaryLabel->setWordWrap(true);
|
||||
m_summaryLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
m_summaryLabel->setOpenExternalLinks(true);
|
||||
m_mainLayout->addSpacing(5);
|
||||
|
||||
// License
|
||||
{
|
||||
QHBoxLayout* licenseHLayout = new QHBoxLayout();
|
||||
licenseHLayout->setMargin(0);
|
||||
licenseHLayout->setAlignment(Qt::AlignLeft);
|
||||
m_mainLayout->addLayout(licenseHLayout);
|
||||
|
||||
QLabel* licenseLabel = CreateStyledLabel(licenseHLayout, s_baseFontSize, s_headerColor);
|
||||
licenseLabel->setText(tr("License: "));
|
||||
|
||||
m_licenseLinkLabel = new LinkLabel("", QUrl(), s_baseFontSize);
|
||||
licenseHLayout->addWidget(m_licenseLinkLabel);
|
||||
|
||||
licenseHLayout->addStretch();
|
||||
|
||||
m_mainLayout->addSpacing(5);
|
||||
}
|
||||
|
||||
// Directory and documentation links
|
||||
{
|
||||
QHBoxLayout* linksHLayout = new QHBoxLayout();
|
||||
linksHLayout->setMargin(0);
|
||||
m_mainLayout->addLayout(linksHLayout);
|
||||
|
||||
QSpacerItem* spacerLeft = new QSpacerItem(0, 0, QSizePolicy::Expanding);
|
||||
linksHLayout->addSpacerItem(spacerLeft);
|
||||
linksHLayout->addStretch();
|
||||
|
||||
m_directoryLinkLabel = new LinkLabel("View in Directory");
|
||||
m_directoryLinkLabel = new LinkLabel(tr("View in Directory"));
|
||||
linksHLayout->addWidget(m_directoryLinkLabel);
|
||||
linksHLayout->addWidget(new QLabel("|"));
|
||||
m_documentationLinkLabel = new LinkLabel("Read Documentation");
|
||||
m_documentationLinkLabel = new LinkLabel(tr("Read Documentation"));
|
||||
linksHLayout->addWidget(m_documentationLinkLabel);
|
||||
|
||||
QSpacerItem* spacerRight = new QSpacerItem(0, 0, QSizePolicy::Expanding);
|
||||
linksHLayout->addSpacerItem(spacerRight);
|
||||
linksHLayout->addStretch();
|
||||
|
||||
m_mainLayout->addSpacing(8);
|
||||
}
|
||||
@@ -144,34 +190,35 @@ namespace O3DE::ProjectManager
|
||||
// Separating line
|
||||
QFrame* hLine = new QFrame();
|
||||
hLine->setFrameShape(QFrame::HLine);
|
||||
hLine->setStyleSheet("color: #666666;");
|
||||
hLine->setObjectName("horizontalSeparatingLine");
|
||||
m_mainLayout->addWidget(hLine);
|
||||
|
||||
m_mainLayout->addSpacing(10);
|
||||
|
||||
// Requirements
|
||||
m_reqirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor);
|
||||
m_requirementsTitleLabel = GemInspector::CreateStyledLabel(m_mainLayout, 16, s_headerColor);
|
||||
|
||||
QHBoxLayout* requrementsLayout = new QHBoxLayout();
|
||||
requrementsLayout->setAlignment(Qt::AlignTop);
|
||||
requrementsLayout->setMargin(0);
|
||||
requrementsLayout->setSpacing(0);
|
||||
QHBoxLayout* requirementsLayout = new QHBoxLayout();
|
||||
requirementsLayout->setAlignment(Qt::AlignTop);
|
||||
requirementsLayout->setMargin(0);
|
||||
requirementsLayout->setSpacing(0);
|
||||
|
||||
m_reqirementsIconLabel = new QLabel();
|
||||
m_reqirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24));
|
||||
requrementsLayout->addWidget(m_reqirementsIconLabel);
|
||||
m_requirementsIconLabel = new QLabel();
|
||||
m_requirementsIconLabel->setPixmap(QIcon(":/Warning.svg").pixmap(24, 24));
|
||||
requirementsLayout->addWidget(m_requirementsIconLabel);
|
||||
|
||||
m_reqirementsTextLabel = GemInspector::CreateStyledLabel(requrementsLayout, 10, s_textColor);
|
||||
m_reqirementsTextLabel->setWordWrap(true);
|
||||
m_reqirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
m_reqirementsTextLabel->setOpenExternalLinks(true);
|
||||
m_requirementsTextLabel = GemInspector::CreateStyledLabel(requirementsLayout, 10, s_textColor);
|
||||
m_requirementsTextLabel->setWordWrap(true);
|
||||
m_requirementsTextLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
m_requirementsTextLabel->setOpenExternalLinks(true);
|
||||
|
||||
QSpacerItem* reqirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::Expanding);
|
||||
requrementsLayout->addSpacerItem(reqirementsSpacer);
|
||||
QSpacerItem* requirementsSpacer = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding);
|
||||
requirementsLayout->addSpacerItem(requirementsSpacer);
|
||||
|
||||
m_mainLayout->addLayout(requrementsLayout);
|
||||
m_mainLayout->addLayout(requirementsLayout);
|
||||
|
||||
m_mainLayout->addSpacing(20);
|
||||
m_requirementsMainSpacer = new QSpacerItem(0, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
m_mainLayout->addSpacerItem(m_requirementsMainSpacer);
|
||||
|
||||
// Depending gems
|
||||
m_dependingGems = new GemsSubWidget();
|
||||
@@ -181,10 +228,10 @@ namespace O3DE::ProjectManager
|
||||
|
||||
// Additional information
|
||||
QLabel* additionalInfoLabel = CreateStyledLabel(m_mainLayout, 14, s_headerColor);
|
||||
additionalInfoLabel->setText("Additional Information");
|
||||
additionalInfoLabel->setText(tr("Additional Information"));
|
||||
|
||||
m_versionLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
|
||||
m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
|
||||
m_binarySizeLabel = CreateStyledLabel(m_mainLayout, 12, s_textColor);
|
||||
m_versionLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor);
|
||||
m_lastUpdatedLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor);
|
||||
m_binarySizeLabel = CreateStyledLabel(m_mainLayout, s_baseFontSize, s_textColor);
|
||||
}
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <QItemSelection>
|
||||
#include <QScrollArea>
|
||||
#include <QWidget>
|
||||
#include <QSpacerItem>
|
||||
#endif
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
|
||||
@@ -36,6 +36,9 @@ namespace O3DE::ProjectManager
|
||||
void Update(const QModelIndex& modelIndex);
|
||||
static QLabel* CreateStyledLabel(QLayout* layout, int fontSize, const QString& colorCodeString);
|
||||
|
||||
// Fonts
|
||||
inline constexpr static int s_baseFontSize = 12;
|
||||
|
||||
// Colors
|
||||
inline constexpr static const char* s_headerColor = "#FFFFFF";
|
||||
inline constexpr static const char* s_textColor = "#DDDDDD";
|
||||
@@ -57,13 +60,15 @@ namespace O3DE::ProjectManager
|
||||
QLabel* m_nameLabel = nullptr;
|
||||
QLabel* m_creatorLabel = nullptr;
|
||||
QLabel* m_summaryLabel = nullptr;
|
||||
LinkLabel* m_licenseLinkLabel = nullptr;
|
||||
LinkLabel* m_directoryLinkLabel = nullptr;
|
||||
LinkLabel* m_documentationLinkLabel = nullptr;
|
||||
|
||||
// Requirements
|
||||
QLabel* m_reqirementsTitleLabel = nullptr;
|
||||
QLabel* m_reqirementsIconLabel = nullptr;
|
||||
QLabel* m_reqirementsTextLabel = nullptr;
|
||||
QLabel* m_requirementsTitleLabel = nullptr;
|
||||
QLabel* m_requirementsIconLabel = nullptr;
|
||||
QLabel* m_requirementsTextLabel = nullptr;
|
||||
QSpacerItem* m_requirementsMainSpacer = nullptr;
|
||||
|
||||
// Depending and conflicting gems
|
||||
GemsSubWidget* m_dependingGems = nullptr;
|
||||
|
||||
@@ -58,6 +58,8 @@ namespace O3DE::ProjectManager
|
||||
item->setData(gemInfo.m_path, RolePath);
|
||||
item->setData(gemInfo.m_requirement, RoleRequirement);
|
||||
item->setData(gemInfo.m_downloadStatus, RoleDownloadStatus);
|
||||
item->setData(gemInfo.m_licenseText, RoleLicenseText);
|
||||
item->setData(gemInfo.m_licenseLink, RoleLicenseLink);
|
||||
|
||||
appendRow(item);
|
||||
|
||||
@@ -248,6 +250,16 @@ namespace O3DE::ProjectManager
|
||||
return modelIndex.data(RoleRequirement).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetLicenseText(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleLicenseText).toString();
|
||||
}
|
||||
|
||||
QString GemModel::GetLicenseLink(const QModelIndex& modelIndex)
|
||||
{
|
||||
return modelIndex.data(RoleLicenseLink).toString();
|
||||
}
|
||||
|
||||
GemModel* GemModel::GetSourceModel(QAbstractItemModel* model)
|
||||
{
|
||||
GemSortFilterProxyModel* proxyModel = qobject_cast<GemSortFilterProxyModel*>(model);
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace O3DE::ProjectManager
|
||||
static QStringList GetFeatures(const QModelIndex& modelIndex);
|
||||
static QString GetPath(const QModelIndex& modelIndex);
|
||||
static QString GetRequirement(const QModelIndex& modelIndex);
|
||||
static QString GetLicenseText(const QModelIndex& modelIndex);
|
||||
static QString GetLicenseLink(const QModelIndex& modelIndex);
|
||||
static GemModel* GetSourceModel(QAbstractItemModel* model);
|
||||
static const GemModel* GetSourceModel(const QAbstractItemModel* model);
|
||||
|
||||
@@ -107,7 +109,9 @@ namespace O3DE::ProjectManager
|
||||
RoleTypes,
|
||||
RolePath,
|
||||
RoleRequirement,
|
||||
RoleDownloadStatus
|
||||
RoleDownloadStatus,
|
||||
RoleLicenseText,
|
||||
RoleLicenseLink
|
||||
};
|
||||
|
||||
QHash<QString, QModelIndex> m_nameToIndexMap;
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace O3DE::ProjectManager
|
||||
m_nameLabel->setObjectName("gemRepoInspectorNameLabel");
|
||||
m_mainLayout->addWidget(m_nameLabel);
|
||||
|
||||
m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(""), 12, this);
|
||||
m_repoLinkLabel = new LinkLabel(tr("Repo Url"), QUrl(), 12, this);
|
||||
m_mainLayout->addWidget(m_repoLinkLabel);
|
||||
m_mainLayout->addSpacing(5);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <QWidget>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include <AzCore/IO/Path/Path_fwd.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
|
||||
namespace O3DE::ProjectManager
|
||||
@@ -67,7 +68,8 @@ namespace O3DE::ProjectManager
|
||||
AZ::Outcome<QString, QString> GetProjectBuildPath(const QString& projectPath);
|
||||
AZ::Outcome<void, QString> OpenCMakeGUI(const QString& projectPath);
|
||||
AZ::Outcome<QString, QString> RunGetPythonScript(const QString& enginePath);
|
||||
|
||||
|
||||
AZ::IO::FixedMaxPath GetEditorDirectory();
|
||||
|
||||
} // namespace ProjectUtils
|
||||
} // namespace O3DE::ProjectManager
|
||||
|
||||
@@ -392,11 +392,11 @@ namespace O3DE::ProjectManager
|
||||
{
|
||||
if (!WarnIfInBuildQueue(projectPath))
|
||||
{
|
||||
AZ::IO::FixedMaxPath executableDirectory = AZ::Utils::GetExecutableDirectory();
|
||||
AZ::IO::FixedMaxPath executableDirectory = ProjectUtils::GetEditorDirectory();
|
||||
AZStd::string executableFilename = "Editor";
|
||||
AZ::IO::FixedMaxPath editorExecutablePath = executableDirectory / (executableFilename + AZ_TRAIT_OS_EXECUTABLE_EXTENSION);
|
||||
auto cmdPath = AZ::IO::FixedMaxPathString::format(
|
||||
"%s -regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(),
|
||||
"%s --regset=\"/Amazon/AzCore/Bootstrap/project_path=%s\"", editorExecutablePath.c_str(),
|
||||
projectPath.toStdString().c_str());
|
||||
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
|
||||
@@ -709,6 +709,8 @@ namespace O3DE::ProjectManager
|
||||
gemInfo.m_requirement = Py_To_String_Optional(data, "requirements", "");
|
||||
gemInfo.m_creator = Py_To_String_Optional(data, "origin", "");
|
||||
gemInfo.m_documentationLink = Py_To_String_Optional(data, "documentation_url", "");
|
||||
gemInfo.m_licenseText = Py_To_String_Optional(data, "license", "Unspecified License");
|
||||
gemInfo.m_licenseLink = Py_To_String_Optional(data, "license_url", "");
|
||||
|
||||
if (gemInfo.m_creator.contains("Open 3D Engine"))
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace AZ
|
||||
}
|
||||
|
||||
ProcessingOverlayWidget::ProcessingOverlayWidget(UI::OverlayWidget* overlay, Layout layout, Uuid traceTag)
|
||||
: QWidget()
|
||||
: QWidget(nullptr, Qt::Tool | Qt::WindowStaysOnTopHint)
|
||||
, m_traceTag(traceTag)
|
||||
, ui(new Ui::ProcessingOverlayWidget())
|
||||
, m_overlay(overlay)
|
||||
|
||||
Reference in New Issue
Block a user