Add XCUI core scroll view validation in new_editor

This commit is contained in:
2026-04-05 21:27:00 +08:00
parent 05debc0499
commit 7812b92992
6 changed files with 518 additions and 34 deletions

View File

@@ -153,6 +153,21 @@ bool TryFindSmallestFilledRectContainingPoint(
return found;
}
std::size_t CountCommandsOfType(
const XCEngine::UI::UIDrawData& drawData,
XCEngine::UI::UIDrawCommandType type) {
std::size_t count = 0u;
for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) {
for (const XCEngine::UI::UIDrawCommand& command : drawList.GetCommands()) {
if (command.type == type) {
++count;
}
}
}
return count;
}
UIScreenFrameInput BuildInputState(std::uint64_t frameIndex = 1u) {
UIScreenFrameInput input = {};
input.viewportRect = XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f);
@@ -308,6 +323,67 @@ TEST(UIRuntimeTest, DocumentHostDoesNotLetExplicitHeightCrushCardContent) {
EXPECT_LE(buttonRect.y + buttonRect.height, cardRect.y + cardRect.height - 8.0f);
}
TEST(UIRuntimeTest, DocumentHostScrollViewClipsOverflowingChildrenAndRespondsToWheelInput) {
TempFileScope viewFile(
"xcui_runtime_scroll_view",
".xcui",
"<View name=\"Scroll View Test\">\n"
" <Column padding=\"18\" gap=\"10\">\n"
" <Card title=\"Console\" subtitle=\"Scroll smoke\" height=\"200\">\n"
" <ScrollView id=\"log-scroll\" height=\"fill\">\n"
" <Column gap=\"8\">\n"
" <Text text=\"Line 01\" />\n"
" <Text text=\"Line 02\" />\n"
" <Text text=\"Line 03\" />\n"
" <Text text=\"Line 04\" />\n"
" <Text text=\"Line 05\" />\n"
" <Text text=\"Line 06\" />\n"
" <Text text=\"Line 07\" />\n"
" <Text text=\"Line 08\" />\n"
" <Text text=\"Line 09\" />\n"
" <Text text=\"Line 10\" />\n"
" <Text text=\"Line 11\" />\n"
" <Text text=\"Line 12\" />\n"
" </Column>\n"
" </ScrollView>\n"
" </Card>\n"
" </Column>\n"
"</View>\n");
UIDocumentScreenHost host = {};
UIScreenPlayer player(host);
ASSERT_TRUE(player.Load(BuildScreenAsset(viewFile.Path(), "runtime.scroll.view")));
UIScreenFrameInput firstInput = BuildInputState(1u);
firstInput.viewportRect = XCEngine::UI::UIRect(0.0f, 0.0f, 480.0f, 320.0f);
const auto& firstFrame = player.Update(firstInput);
const auto* line01Before = FindTextCommand(firstFrame.drawData, "Line 01");
ASSERT_NE(line01Before, nullptr);
const float line01BeforeY = line01Before->position.y;
EXPECT_GT(CountCommandsOfType(firstFrame.drawData, XCEngine::UI::UIDrawCommandType::PushClipRect), 0u);
EXPECT_GT(CountCommandsOfType(firstFrame.drawData, XCEngine::UI::UIDrawCommandType::PopClipRect), 0u);
UIScreenFrameInput secondInput = BuildInputState(2u);
secondInput.viewportRect = firstInput.viewportRect;
XCEngine::UI::UIInputEvent wheelEvent = {};
wheelEvent.type = XCEngine::UI::UIInputEventType::PointerWheel;
wheelEvent.position = XCEngine::UI::UIPoint(90.0f, 130.0f);
wheelEvent.wheelDelta = -120.0f;
secondInput.events.push_back(wheelEvent);
const auto& secondFrame = player.Update(secondInput);
const auto* line01After = FindTextCommand(secondFrame.drawData, "Line 01");
ASSERT_NE(line01After, nullptr);
const float line01AfterY = line01After->position.y;
EXPECT_LT(line01AfterY, line01BeforeY);
UIScreenFrameInput thirdInput = BuildInputState(3u);
thirdInput.viewportRect = firstInput.viewportRect;
const auto& thirdFrame = player.Update(thirdInput);
const auto* line01Persisted = FindTextCommand(thirdFrame.drawData, "Line 01");
ASSERT_NE(line01Persisted, nullptr);
EXPECT_FLOAT_EQ(line01Persisted->position.y, line01AfterY);
}
TEST(UIRuntimeTest, ScreenPlayerConsumeLastFrameReturnsDetachedPacketAndClearsBorrowedState) {
TempFileScope viewFile("xcui_runtime_consume_player", ".xcui", BuildViewMarkup("Runtime Consume"));
UIDocumentScreenHost host = {};