Add XCUI input state validation sandbox batch
This commit is contained in:
@@ -153,6 +153,21 @@ bool TryFindSmallestFilledRectContainingPoint(
|
||||
return found;
|
||||
}
|
||||
|
||||
bool TryFindFilledRectForText(
|
||||
const XCEngine::UI::UIDrawData& drawData,
|
||||
const std::string& text,
|
||||
XCEngine::UI::UIRect& outRect) {
|
||||
const auto* textCommand = FindTextCommand(drawData, text);
|
||||
return textCommand != nullptr &&
|
||||
TryFindSmallestFilledRectContainingPoint(drawData, textCommand->position, outRect);
|
||||
}
|
||||
|
||||
XCEngine::UI::UIPoint GetRectCenter(const XCEngine::UI::UIRect& rect) {
|
||||
return XCEngine::UI::UIPoint(
|
||||
rect.x + rect.width * 0.5f,
|
||||
rect.y + rect.height * 0.5f);
|
||||
}
|
||||
|
||||
std::size_t CountCommandsOfType(
|
||||
const XCEngine::UI::UIDrawData& drawData,
|
||||
XCEngine::UI::UIDrawCommandType type) {
|
||||
@@ -384,6 +399,122 @@ TEST(UIRuntimeTest, DocumentHostScrollViewClipsOverflowingChildrenAndRespondsToW
|
||||
EXPECT_FLOAT_EQ(line01Persisted->position.y, line01AfterY);
|
||||
}
|
||||
|
||||
TEST(UIRuntimeTest, DocumentHostTracksHoverFocusAndPointerCaptureAcrossFrames) {
|
||||
TempFileScope viewFile(
|
||||
"xcui_runtime_input_states",
|
||||
".xcui",
|
||||
"<View name=\"Input State Test\">\n"
|
||||
" <Column padding=\"18\" gap=\"10\">\n"
|
||||
" <Button id=\"input-hover\" text=\"Hover / Focus\" />\n"
|
||||
" <Button id=\"input-capture\" text=\"Pointer Capture\" capturePointer=\"true\" />\n"
|
||||
" <Button id=\"input-route\" text=\"Route Target\" />\n"
|
||||
" </Column>\n"
|
||||
"</View>\n");
|
||||
UIDocumentScreenHost host = {};
|
||||
UIScreenPlayer player(host);
|
||||
|
||||
ASSERT_TRUE(player.Load(BuildScreenAsset(viewFile.Path(), "runtime.input.states")));
|
||||
|
||||
UIScreenFrameInput firstInput = BuildInputState(1u);
|
||||
firstInput.viewportRect = XCEngine::UI::UIRect(0.0f, 0.0f, 520.0f, 260.0f);
|
||||
const auto& firstFrame = player.Update(firstInput);
|
||||
const auto* hoverButtonText = FindTextCommand(firstFrame.drawData, "Hover / Focus");
|
||||
const auto* captureButtonText = FindTextCommand(firstFrame.drawData, "Pointer Capture");
|
||||
const auto* routeButtonText = FindTextCommand(firstFrame.drawData, "Route Target");
|
||||
ASSERT_NE(hoverButtonText, nullptr);
|
||||
ASSERT_NE(captureButtonText, nullptr);
|
||||
ASSERT_NE(routeButtonText, nullptr);
|
||||
|
||||
XCEngine::UI::UIRect hoverButtonRect = {};
|
||||
XCEngine::UI::UIRect captureButtonRect = {};
|
||||
XCEngine::UI::UIRect routeButtonRect = {};
|
||||
ASSERT_TRUE(TryFindFilledRectForText(firstFrame.drawData, "Hover / Focus", hoverButtonRect));
|
||||
ASSERT_TRUE(TryFindFilledRectForText(firstFrame.drawData, "Pointer Capture", captureButtonRect));
|
||||
ASSERT_TRUE(TryFindFilledRectForText(firstFrame.drawData, "Route Target", routeButtonRect));
|
||||
|
||||
const XCEngine::UI::UIPoint hoverButtonPoint = GetRectCenter(hoverButtonRect);
|
||||
const XCEngine::UI::UIPoint captureButtonPoint = GetRectCenter(captureButtonRect);
|
||||
const XCEngine::UI::UIPoint routeButtonPoint = GetRectCenter(routeButtonRect);
|
||||
|
||||
SCOPED_TRACE(
|
||||
std::string("hoverPos=") + std::to_string(hoverButtonText->position.x) + "," + std::to_string(hoverButtonText->position.y) +
|
||||
" hoverRect=" + std::to_string(hoverButtonRect.x) + "," + std::to_string(hoverButtonRect.y) + "," +
|
||||
std::to_string(hoverButtonRect.width) + "," + std::to_string(hoverButtonRect.height) +
|
||||
" capturePos=" + std::to_string(captureButtonText->position.x) + "," + std::to_string(captureButtonText->position.y) +
|
||||
" captureRect=" + std::to_string(captureButtonRect.x) + "," + std::to_string(captureButtonRect.y) + "," +
|
||||
std::to_string(captureButtonRect.width) + "," + std::to_string(captureButtonRect.height) +
|
||||
" routePos=" + std::to_string(routeButtonText->position.x) + "," + std::to_string(routeButtonText->position.y) +
|
||||
" routeRect=" + std::to_string(routeButtonRect.x) + "," + std::to_string(routeButtonRect.y) + "," +
|
||||
std::to_string(routeButtonRect.width) + "," + std::to_string(routeButtonRect.height));
|
||||
|
||||
UIScreenFrameInput hoverInput = BuildInputState(2u);
|
||||
hoverInput.viewportRect = firstInput.viewportRect;
|
||||
XCEngine::UI::UIInputEvent hoverEvent = {};
|
||||
hoverEvent.type = XCEngine::UI::UIInputEventType::PointerMove;
|
||||
hoverEvent.position = hoverButtonPoint;
|
||||
hoverInput.events.push_back(hoverEvent);
|
||||
player.Update(hoverInput);
|
||||
const auto& afterHover = host.GetInputDebugSnapshot();
|
||||
EXPECT_NE(afterHover.hoveredStateKey.find("/input-hover"), std::string::npos);
|
||||
EXPECT_TRUE(afterHover.focusedStateKey.empty());
|
||||
|
||||
UIScreenFrameInput captureDownInput = BuildInputState(3u);
|
||||
captureDownInput.viewportRect = firstInput.viewportRect;
|
||||
XCEngine::UI::UIInputEvent captureDownEvent = {};
|
||||
captureDownEvent.type = XCEngine::UI::UIInputEventType::PointerButtonDown;
|
||||
captureDownEvent.pointerButton = XCEngine::UI::UIPointerButton::Left;
|
||||
captureDownEvent.position = captureButtonPoint;
|
||||
captureDownInput.events.push_back(captureDownEvent);
|
||||
player.Update(captureDownInput);
|
||||
const auto& afterCaptureDown = host.GetInputDebugSnapshot();
|
||||
SCOPED_TRACE(
|
||||
std::string("afterCaptureDown hovered=") + afterCaptureDown.hoveredStateKey +
|
||||
" focused=" + afterCaptureDown.focusedStateKey +
|
||||
" active=" + afterCaptureDown.activeStateKey +
|
||||
" capture=" + afterCaptureDown.captureStateKey +
|
||||
" lastKind=" + afterCaptureDown.lastTargetKind +
|
||||
" lastTarget=" + afterCaptureDown.lastTargetStateKey +
|
||||
" result=" + afterCaptureDown.lastResult);
|
||||
EXPECT_NE(afterCaptureDown.focusedStateKey.find("/input-capture"), std::string::npos);
|
||||
EXPECT_NE(afterCaptureDown.activeStateKey.find("/input-capture"), std::string::npos);
|
||||
EXPECT_NE(afterCaptureDown.captureStateKey.find("/input-capture"), std::string::npos);
|
||||
|
||||
UIScreenFrameInput dragInput = BuildInputState(4u);
|
||||
dragInput.viewportRect = firstInput.viewportRect;
|
||||
XCEngine::UI::UIInputEvent dragEvent = {};
|
||||
dragEvent.type = XCEngine::UI::UIInputEventType::PointerMove;
|
||||
dragEvent.position = routeButtonPoint;
|
||||
dragInput.events.push_back(dragEvent);
|
||||
player.Update(dragInput);
|
||||
const auto& afterDrag = host.GetInputDebugSnapshot();
|
||||
SCOPED_TRACE(
|
||||
std::string("afterDrag hovered=") + afterDrag.hoveredStateKey +
|
||||
" focused=" + afterDrag.focusedStateKey +
|
||||
" active=" + afterDrag.activeStateKey +
|
||||
" capture=" + afterDrag.captureStateKey +
|
||||
" lastKind=" + afterDrag.lastTargetKind +
|
||||
" lastTarget=" + afterDrag.lastTargetStateKey +
|
||||
" result=" + afterDrag.lastResult);
|
||||
EXPECT_NE(afterDrag.hoveredStateKey.find("/input-route"), std::string::npos);
|
||||
EXPECT_NE(afterDrag.captureStateKey.find("/input-capture"), std::string::npos);
|
||||
EXPECT_EQ(afterDrag.lastTargetKind, "Captured");
|
||||
EXPECT_NE(afterDrag.lastTargetStateKey.find("/input-capture"), std::string::npos);
|
||||
|
||||
UIScreenFrameInput releaseInput = BuildInputState(5u);
|
||||
releaseInput.viewportRect = firstInput.viewportRect;
|
||||
XCEngine::UI::UIInputEvent releaseEvent = {};
|
||||
releaseEvent.type = XCEngine::UI::UIInputEventType::PointerButtonUp;
|
||||
releaseEvent.pointerButton = XCEngine::UI::UIPointerButton::Left;
|
||||
releaseEvent.position = routeButtonPoint;
|
||||
releaseInput.events.push_back(releaseEvent);
|
||||
player.Update(releaseInput);
|
||||
const auto& afterRelease = host.GetInputDebugSnapshot();
|
||||
EXPECT_TRUE(afterRelease.activeStateKey.empty());
|
||||
EXPECT_TRUE(afterRelease.captureStateKey.empty());
|
||||
EXPECT_NE(afterRelease.focusedStateKey.find("/input-capture"), std::string::npos);
|
||||
EXPECT_NE(afterRelease.hoveredStateKey.find("/input-route"), std::string::npos);
|
||||
}
|
||||
|
||||
TEST(UIRuntimeTest, ScreenPlayerConsumeLastFrameReturnsDetachedPacketAndClearsBorrowedState) {
|
||||
TempFileScope viewFile("xcui_runtime_consume_player", ".xcui", BuildViewMarkup("Runtime Consume"));
|
||||
UIDocumentScreenHost host = {};
|
||||
|
||||
Reference in New Issue
Block a user