#include #include "panels/XCUILayoutLabPanel.h" #include "XCUIBackend/ImGuiXCUIInputSource.h" #include "XCUIBackend/XCUIHostedPreviewPresenter.h" #include "XCUIBackend/XCUIPanelCanvasHost.h" #include #include #include #include namespace { using XCEngine::Editor::XCUIBackend::IXCUIHostedPreviewPresenter; using XCEngine::Editor::XCUIBackend::IXCUIPanelCanvasHost; using XCEngine::Editor::XCUIBackend::ImGuiXCUIInputSnapshotSource; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewFrame; using XCEngine::Editor::XCUIBackend::XCUIHostedPreviewStats; using XCEngine::Editor::XCUIBackend::XCUIPanelCanvasRequest; using XCEngine::Editor::XCUIBackend::XCUIPanelCanvasSession; using XCEngine::NewEditor::XCUILayoutLabPanel; class ImGuiContextScope { public: ImGuiContextScope() { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGui::StyleColorsDark(); } ~ImGuiContextScope() { ImGui::DestroyContext(); } }; void PrepareImGui(float width = 1280.0f, float height = 900.0f) { ImGuiIO& io = ImGui::GetIO(); io.DisplaySize = ImVec2(width, height); io.DeltaTime = 1.0f / 60.0f; unsigned char* fontPixels = nullptr; int fontWidth = 0; int fontHeight = 0; io.Fonts->GetTexDataAsRGBA32(&fontPixels, &fontWidth, &fontHeight); io.Fonts->SetTexID(static_cast(1)); } class StubHostedPreviewPresenter final : public IXCUIHostedPreviewPresenter { public: bool Present(const XCUIHostedPreviewFrame& frame) override { m_lastStats = {}; m_lastStats.presented = frame.drawData != nullptr; return m_lastStats.presented; } const XCUIHostedPreviewStats& GetLastStats() const override { return m_lastStats; } private: XCUIHostedPreviewStats m_lastStats = {}; }; class StubCanvasHost final : public IXCUIPanelCanvasHost { public: const char* GetDebugName() const override { return "StubCanvasHost"; } XCUIPanelCanvasSession BeginCanvas(const XCUIPanelCanvasRequest&) override { return m_session; } void DrawFilledRect( const XCEngine::UI::UIRect&, const XCEngine::UI::UIColor&, float) override { } void DrawOutlineRect( const XCEngine::UI::UIRect&, const XCEngine::UI::UIColor&, float, float) override { } void DrawText( const XCEngine::UI::UIPoint&, std::string_view, const XCEngine::UI::UIColor&, float) override { } void EndCanvas() override { } void SetPointerPosition(const XCEngine::UI::UIPoint& position) { m_session.pointerPosition = position; } void SetHovered(bool hovered) { m_session.hovered = hovered; } void SetWindowFocused(bool focused) { m_session.windowFocused = focused; } private: XCUIPanelCanvasSession m_session = { XCEngine::UI::UIRect(0.0f, 0.0f, 960.0f, 640.0f), XCEngine::UI::UIRect(0.0f, 0.0f, 960.0f, 640.0f), XCEngine::UI::UIPoint(120.0f, 120.0f), true, true, true }; }; void RenderPanelFrame( XCUILayoutLabPanel& panel, ImGuiContextScope&, const std::function& configureIo = nullptr) { PrepareImGui(); if (configureIo) { configureIo(ImGui::GetIO()); } ImGui::NewFrame(); panel.Render(); ImGui::Render(); } void ClickElement( XCUILayoutLabPanel& panel, StubCanvasHost& canvasHost, const std::string& elementId, ImGuiContextScope& contextScope) { XCEngine::UI::UIRect elementRect = {}; ASSERT_TRUE(panel.TryGetElementRect(elementId, elementRect)); const XCEngine::UI::UIPoint clickPoint( elementRect.x + elementRect.width * 0.5f, elementRect.y + elementRect.height * 0.5f); canvasHost.SetPointerPosition(clickPoint); canvasHost.SetHovered(true); RenderPanelFrame( panel, contextScope, [clickPoint](ImGuiIO& io) { io.AddMousePosEvent(clickPoint.x, clickPoint.y); io.AddMouseButtonEvent(ImGuiMouseButton_Left, true); }); RenderPanelFrame( panel, contextScope, [clickPoint](ImGuiIO& io) { io.AddMousePosEvent(clickPoint.x, clickPoint.y); io.AddMouseButtonEvent(ImGuiMouseButton_Left, false); }); } void PressKeyOnce( XCUILayoutLabPanel& panel, ImGuiContextScope& contextScope, ImGuiKey key) { RenderPanelFrame( panel, contextScope, [key](ImGuiIO& io) { io.AddKeyEvent(key, true); }); RenderPanelFrame( panel, contextScope, [key](ImGuiIO& io) { io.AddKeyEvent(key, false); }); } TEST(NewEditorXCUILayoutLabPanelTest, MapsPreviousNextHomeAndEndIntoRuntimeNavigation) { ImGuiContextScope contextScope; auto previewPresenter = std::make_unique(); auto canvasHost = std::make_unique(); StubCanvasHost* canvasHostPtr = canvasHost.get(); ImGuiXCUIInputSnapshotSource inputSource(nullptr); XCUILayoutLabPanel panel(&inputSource, std::move(previewPresenter), std::move(canvasHost)); panel.SetHostedPreviewEnabled(false); RenderPanelFrame(panel, contextScope); ClickElement(panel, *canvasHostPtr, "assetLighting", contextScope); ASSERT_EQ(panel.GetFrameResult().stats.selectedElementId, "assetLighting"); canvasHostPtr->SetHovered(false); canvasHostPtr->SetWindowFocused(true); PressKeyOnce(panel, contextScope, ImGuiKey_DownArrow); EXPECT_EQ(panel.GetFrameResult().stats.selectedElementId, "assetMaterials"); PressKeyOnce(panel, contextScope, ImGuiKey_UpArrow); EXPECT_EQ(panel.GetFrameResult().stats.selectedElementId, "assetLighting"); PressKeyOnce(panel, contextScope, ImGuiKey_End); const std::string endSelection = panel.GetFrameResult().stats.selectedElementId; ASSERT_FALSE(endSelection.empty()); EXPECT_NE(endSelection, "assetLighting"); PressKeyOnce(panel, contextScope, ImGuiKey_Home); EXPECT_EQ(panel.GetFrameResult().stats.selectedElementId, "assetLighting"); } TEST(NewEditorXCUILayoutLabPanelTest, MapsCollapseAndExpandIntoRuntimeNavigation) { ImGuiContextScope contextScope; auto previewPresenter = std::make_unique(); auto canvasHost = std::make_unique(); StubCanvasHost* canvasHostPtr = canvasHost.get(); ImGuiXCUIInputSnapshotSource inputSource(nullptr); XCUILayoutLabPanel panel(&inputSource, std::move(previewPresenter), std::move(canvasHost)); panel.SetHostedPreviewEnabled(false); RenderPanelFrame(panel, contextScope); ClickElement(panel, *canvasHostPtr, "treeScenes", contextScope); ASSERT_EQ(panel.GetFrameResult().stats.selectedElementId, "treeScenes"); canvasHostPtr->SetHovered(false); canvasHostPtr->SetWindowFocused(true); PressKeyOnce(panel, contextScope, ImGuiKey_LeftArrow); EXPECT_EQ(panel.GetFrameResult().stats.selectedElementId, "treeAssetsRoot"); PressKeyOnce(panel, contextScope, ImGuiKey_LeftArrow); EXPECT_EQ(panel.GetFrameResult().stats.selectedElementId, "treeAssetsRoot"); EXPECT_EQ(panel.GetFrameResult().stats.expandedTreeItemCount, 0u); PressKeyOnce(panel, contextScope, ImGuiKey_RightArrow); EXPECT_EQ(panel.GetFrameResult().stats.selectedElementId, "treeAssetsRoot"); EXPECT_EQ(panel.GetFrameResult().stats.expandedTreeItemCount, 1u); PressKeyOnce(panel, contextScope, ImGuiKey_RightArrow); EXPECT_EQ(panel.GetFrameResult().stats.selectedElementId, "treeScenes"); } TEST(NewEditorXCUILayoutLabPanelTest, DefaultFallbackDoesNotCreateImplicitHostedPreviewPresenter) { ImGuiContextScope contextScope; XCUILayoutLabPanel panel(nullptr); RenderPanelFrame(panel, contextScope); EXPECT_FALSE(panel.IsUsingNativeHostedPreview()); EXPECT_FALSE(panel.GetLastPreviewStats().presented); EXPECT_EQ(panel.GetLastPreviewStats().submittedDrawListCount, 0u); EXPECT_EQ(panel.GetLastPreviewStats().submittedCommandCount, 0u); } } // namespace