Expand XCUI layout lab editor widgets
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
#include <XCEngine/Resources/UI/UIDocuments.h>
|
||||
#include <XCEngine/UI/Core/UIContext.h>
|
||||
|
||||
namespace {
|
||||
@@ -15,6 +17,10 @@ using XCEngine::UI::UIElementChangeKind;
|
||||
using XCEngine::UI::UIElementId;
|
||||
using XCEngine::UI::UIElementNode;
|
||||
using XCEngine::UI::UIElementTree;
|
||||
using XCEngine::Resources::UIDocumentKind;
|
||||
using XCEngine::Resources::UIDocumentModel;
|
||||
using XCEngine::Resources::UISchema;
|
||||
using XCEngine::Resources::UIView;
|
||||
|
||||
class TestViewModel : public RevisionedViewModelBase {
|
||||
public:
|
||||
@@ -194,4 +200,41 @@ TEST(UICoreTest, RebuildFailsWhenElementScopesRemainOpen) {
|
||||
EXPECT_EQ(context.GetElementTree().GetNodeCount(), 0u);
|
||||
}
|
||||
|
||||
TEST(UICoreTest, UIDocumentResourcesAcceptMovedDocumentModels) {
|
||||
UIDocumentModel viewDocument = {};
|
||||
viewDocument.kind = UIDocumentKind::View;
|
||||
viewDocument.sourcePath = "Assets/UI/Test.xcui";
|
||||
viewDocument.displayName = "TestView";
|
||||
viewDocument.rootNode.tagName = "View";
|
||||
viewDocument.valid = true;
|
||||
|
||||
UIView view = {};
|
||||
XCEngine::Resources::IResource::ConstructParams params = {};
|
||||
params.name = "TestView";
|
||||
params.path = viewDocument.sourcePath;
|
||||
params.guid = XCEngine::Resources::ResourceGUID::Generate(params.path);
|
||||
view.Initialize(params);
|
||||
view.SetDocumentModel(std::move(viewDocument));
|
||||
EXPECT_EQ(view.GetRootNode().tagName, "View");
|
||||
EXPECT_EQ(view.GetSourcePath(), "Assets/UI/Test.xcui");
|
||||
|
||||
UIDocumentModel schemaDocument = {};
|
||||
schemaDocument.kind = UIDocumentKind::Schema;
|
||||
schemaDocument.sourcePath = "Assets/UI/Test.xcschema";
|
||||
schemaDocument.displayName = "TestSchema";
|
||||
schemaDocument.rootNode.tagName = "Schema";
|
||||
schemaDocument.schemaDefinition.name = "TestSchema";
|
||||
schemaDocument.schemaDefinition.valid = true;
|
||||
schemaDocument.valid = true;
|
||||
|
||||
UISchema schema = {};
|
||||
params.name = "TestSchema";
|
||||
params.path = schemaDocument.sourcePath;
|
||||
params.guid = XCEngine::Resources::ResourceGUID::Generate(params.path);
|
||||
schema.Initialize(params);
|
||||
schema.SetDocumentModel(std::move(schemaDocument));
|
||||
EXPECT_TRUE(schema.GetSchemaDefinition().valid);
|
||||
EXPECT_EQ(schema.GetSchemaDefinition().name, "TestSchema");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -4,323 +4,140 @@
|
||||
#include <XCEngine/UI/Runtime/UIScreenPlayer.h>
|
||||
#include <XCEngine/UI/Runtime/UISystem.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using XCEngine::UI::UIColor;
|
||||
using XCEngine::UI::UIDrawList;
|
||||
using XCEngine::UI::UIInputEvent;
|
||||
using XCEngine::UI::UIInputEventType;
|
||||
using XCEngine::UI::UIPoint;
|
||||
using XCEngine::UI::UIRect;
|
||||
using XCEngine::UI::Runtime::IUIScreenDocumentHost;
|
||||
using XCEngine::UI::Runtime::UIScreenAsset;
|
||||
using XCEngine::UI::Runtime::UIScreenDocument;
|
||||
using XCEngine::UI::Runtime::UIScreenFrameInput;
|
||||
using XCEngine::UI::Runtime::UIScreenFrameResult;
|
||||
using XCEngine::UI::Runtime::UIScreenLayerId;
|
||||
using XCEngine::UI::Runtime::UIScreenLayerOptions;
|
||||
using XCEngine::UI::Runtime::UIScreenLoadResult;
|
||||
using XCEngine::UI::Runtime::UIScreenPlayer;
|
||||
using XCEngine::UI::Runtime::UIDocumentScreenHost;
|
||||
using XCEngine::UI::Runtime::UISystemFrameResult;
|
||||
using XCEngine::UI::Runtime::UISystem;
|
||||
|
||||
class FakeScreenDocumentHost final : public IUIScreenDocumentHost {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class TempFileScope {
|
||||
public:
|
||||
struct BuildCall {
|
||||
std::string displayName = {};
|
||||
std::size_t inputEventCount = 0;
|
||||
std::uint64_t frameIndex = 0;
|
||||
};
|
||||
|
||||
UIScreenLoadResult LoadScreen(const UIScreenAsset& asset) override {
|
||||
++loadCount;
|
||||
lastLoadedAsset = asset;
|
||||
|
||||
UIScreenLoadResult result = {};
|
||||
if (!asset.IsValid()) {
|
||||
result.errorMessage = "Invalid screen asset.";
|
||||
return result;
|
||||
}
|
||||
|
||||
result.succeeded = true;
|
||||
result.document.sourcePath = asset.documentPath;
|
||||
result.document.displayName = asset.screenId.empty() ? asset.documentPath : asset.screenId;
|
||||
result.document.viewDocument.valid = true;
|
||||
result.document.dependencies.push_back(asset.themePath);
|
||||
return result;
|
||||
TempFileScope(std::string stem, std::string extension, std::string contents) {
|
||||
const auto uniqueId = std::to_string(
|
||||
std::chrono::steady_clock::now().time_since_epoch().count());
|
||||
m_path = fs::temp_directory_path() / (std::move(stem) + "_" + uniqueId + std::move(extension));
|
||||
std::ofstream output(m_path, std::ios::binary | std::ios::trunc);
|
||||
output << contents;
|
||||
}
|
||||
|
||||
UIScreenFrameResult BuildFrame(
|
||||
const UIScreenDocument& document,
|
||||
const UIScreenFrameInput& input) override {
|
||||
++buildCount;
|
||||
lastBuiltDocument = document;
|
||||
lastFrameInput = input;
|
||||
|
||||
UIScreenFrameResult result = {};
|
||||
UIDrawList& drawList = result.drawData.EmplaceDrawList(document.displayName);
|
||||
drawList.AddFilledRect(input.viewportRect, UIColor(0.2f, 0.3f, 0.4f, 1.0f), 4.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(input.viewportRect.x + 8.0f, input.viewportRect.y + 8.0f),
|
||||
document.displayName,
|
||||
UIColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||
16.0f);
|
||||
buildCalls.push_back(BuildCall{
|
||||
document.displayName,
|
||||
input.events.size(),
|
||||
input.frameIndex
|
||||
});
|
||||
return result;
|
||||
~TempFileScope() {
|
||||
std::error_code ec;
|
||||
fs::remove(m_path, ec);
|
||||
}
|
||||
|
||||
std::size_t loadCount = 0;
|
||||
std::size_t buildCount = 0;
|
||||
UIScreenAsset lastLoadedAsset = {};
|
||||
UIScreenDocument lastBuiltDocument = {};
|
||||
UIScreenFrameInput lastFrameInput = {};
|
||||
std::vector<BuildCall> buildCalls = {};
|
||||
const fs::path& Path() const {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
private:
|
||||
fs::path m_path = {};
|
||||
};
|
||||
|
||||
UIScreenAsset MakeAsset() {
|
||||
UIScreenAsset asset = {};
|
||||
asset.screenId = "MainMenu";
|
||||
asset.documentPath = "Assets/UI/MainMenu.xcui";
|
||||
asset.themePath = "Assets/UI/MainMenu.xctheme";
|
||||
return asset;
|
||||
std::string BuildViewMarkup(const char* heroTitle, const char* overlayText = nullptr) {
|
||||
std::string markup =
|
||||
"<View name=\"Runtime Screen\">\n"
|
||||
" <Column id=\"root\" padding=\"18\" gap=\"10\">\n"
|
||||
" <Card id=\"hero\" title=\"" + std::string(heroTitle) + "\" subtitle=\"Shared XCUI runtime layer\" />\n"
|
||||
" <Text id=\"status\" text=\"Ready for play\" />\n"
|
||||
" <Row id=\"actions\" gap=\"12\">\n"
|
||||
" <Button id=\"start\" text=\"Start\" />\n"
|
||||
" <Button id=\"options\" text=\"Options\" />\n"
|
||||
" </Row>\n";
|
||||
if (overlayText != nullptr) {
|
||||
markup += " <Card id=\"overlay\" title=\"" + std::string(overlayText) + "\" tone=\"accent\" />\n";
|
||||
}
|
||||
markup +=
|
||||
" </Column>\n"
|
||||
"</View>\n";
|
||||
return markup;
|
||||
}
|
||||
|
||||
UIScreenFrameInput MakeFrameInput(std::uint64_t frameIndex = 7) {
|
||||
UIScreenAsset BuildScreenAsset(const fs::path& viewPath, const char* screenId) {
|
||||
UIScreenAsset screen = {};
|
||||
screen.screenId = screenId;
|
||||
screen.documentPath = viewPath.string();
|
||||
return screen;
|
||||
}
|
||||
|
||||
bool DrawDataContainsText(
|
||||
const XCEngine::UI::UIDrawData& drawData,
|
||||
const std::string& text) {
|
||||
for (const XCEngine::UI::UIDrawList& drawList : drawData.GetDrawLists()) {
|
||||
for (const XCEngine::UI::UIDrawCommand& command : drawList.GetCommands()) {
|
||||
if (command.type == XCEngine::UI::UIDrawCommandType::Text &&
|
||||
command.text == text) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
UIScreenFrameInput BuildInputState(std::uint64_t frameIndex = 1u) {
|
||||
UIScreenFrameInput input = {};
|
||||
input.viewportRect = UIRect(10.0f, 20.0f, 320.0f, 180.0f);
|
||||
input.deltaTimeSeconds = 1.0 / 60.0;
|
||||
input.viewportRect = XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f);
|
||||
input.frameIndex = frameIndex;
|
||||
input.focused = true;
|
||||
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::PointerMove;
|
||||
event.position = UIPoint(42.0f, 64.0f);
|
||||
input.events.push_back(event);
|
||||
return input;
|
||||
}
|
||||
|
||||
void WriteTextFile(const fs::path& path, const char* contents) {
|
||||
fs::create_directories(path.parent_path());
|
||||
std::ofstream output(path, std::ios::binary | std::ios::trunc);
|
||||
ASSERT_TRUE(output.is_open());
|
||||
output << contents;
|
||||
ASSERT_TRUE(static_cast<bool>(output));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(UIRuntimeTest, ScreenPlayerLoadsAssetAndDocumentMetadata) {
|
||||
FakeScreenDocumentHost host = {};
|
||||
TEST(UIRuntimeTest, ScreenPlayerBuildsDrawDataFromDocumentTree) {
|
||||
TempFileScope viewFile("xcui_runtime_screen", ".xcui", BuildViewMarkup("Runtime HUD"));
|
||||
UIDocumentScreenHost host = {};
|
||||
UIScreenPlayer player(host);
|
||||
|
||||
ASSERT_TRUE(player.Load(MakeAsset()));
|
||||
ASSERT_TRUE(player.IsLoaded());
|
||||
ASSERT_NE(player.GetAsset(), nullptr);
|
||||
ASSERT_NE(player.GetDocument(), nullptr);
|
||||
EXPECT_EQ(player.GetAsset()->documentPath, "Assets/UI/MainMenu.xcui");
|
||||
EXPECT_EQ(player.GetDocument()->displayName, "MainMenu");
|
||||
EXPECT_EQ(host.loadCount, 1u);
|
||||
}
|
||||
ASSERT_TRUE(player.Load(BuildScreenAsset(viewFile.Path(), "runtime.main_menu")));
|
||||
|
||||
TEST(UIRuntimeTest, ScreenPlayerUpdateBuildsFrameAndTracksStats) {
|
||||
FakeScreenDocumentHost host = {};
|
||||
UIScreenPlayer player(host);
|
||||
ASSERT_TRUE(player.Load(MakeAsset()));
|
||||
|
||||
const UIScreenFrameResult& result = player.Update(MakeFrameInput(12));
|
||||
|
||||
EXPECT_TRUE(result.errorMessage.empty());
|
||||
EXPECT_TRUE(result.stats.documentLoaded);
|
||||
EXPECT_EQ(result.stats.drawListCount, 1u);
|
||||
EXPECT_EQ(result.stats.commandCount, 2u);
|
||||
EXPECT_EQ(result.stats.inputEventCount, 1u);
|
||||
EXPECT_EQ(result.stats.presentedFrameIndex, 12u);
|
||||
const auto& frame = player.Update(BuildInputState());
|
||||
EXPECT_TRUE(frame.stats.documentLoaded);
|
||||
EXPECT_EQ(frame.stats.nodeCount, 7u);
|
||||
EXPECT_EQ(frame.stats.drawListCount, frame.drawData.GetDrawListCount());
|
||||
EXPECT_EQ(frame.stats.commandCount, frame.drawData.GetTotalCommandCount());
|
||||
EXPECT_GE(frame.stats.textCommandCount, 5u);
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Runtime HUD"));
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Ready for play"));
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Start"));
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Options"));
|
||||
EXPECT_EQ(player.GetPresentedFrameCount(), 1u);
|
||||
EXPECT_EQ(host.buildCount, 1u);
|
||||
EXPECT_EQ(host.lastBuiltDocument.displayName, "MainMenu");
|
||||
EXPECT_EQ(host.lastFrameInput.viewportRect.width, 320.0f);
|
||||
}
|
||||
|
||||
TEST(UIRuntimeTest, ScreenPlayerWithoutLoadedDocumentReturnsNotLoadedFrame) {
|
||||
FakeScreenDocumentHost host = {};
|
||||
UIScreenPlayer player(host);
|
||||
TEST(UIRuntimeTest, UISystemForwardsActiveScreenToPlayer) {
|
||||
TempFileScope baseView("xcui_runtime_base", ".xcui", BuildViewMarkup("Base Screen"));
|
||||
TempFileScope overlayView("xcui_runtime_overlay", ".xcui", BuildViewMarkup("Overlay Screen", "Modal Dialog"));
|
||||
|
||||
const UIScreenFrameResult& result = player.Update(MakeFrameInput());
|
||||
|
||||
EXPECT_FALSE(result.stats.documentLoaded);
|
||||
EXPECT_TRUE(result.drawData.Empty());
|
||||
EXPECT_FALSE(result.errorMessage.empty());
|
||||
EXPECT_EQ(player.GetPresentedFrameCount(), 0u);
|
||||
EXPECT_EQ(host.buildCount, 0u);
|
||||
}
|
||||
|
||||
TEST(UIRuntimeTest, UISystemTicksAllCreatedPlayers) {
|
||||
FakeScreenDocumentHost host = {};
|
||||
UISystem system(host);
|
||||
UIScreenPlayer& playerA = system.CreatePlayer();
|
||||
UIScreenPlayer& playerB = system.CreatePlayer();
|
||||
|
||||
ASSERT_TRUE(playerA.Load(MakeAsset()));
|
||||
|
||||
UIScreenAsset hudAsset = MakeAsset();
|
||||
hudAsset.screenId = "HUD";
|
||||
hudAsset.documentPath = "Assets/UI/Hud.xcui";
|
||||
ASSERT_TRUE(playerB.Load(hudAsset));
|
||||
|
||||
system.Tick(MakeFrameInput(21));
|
||||
|
||||
EXPECT_EQ(system.GetPlayerCount(), 2u);
|
||||
EXPECT_EQ(host.loadCount, 2u);
|
||||
EXPECT_EQ(host.buildCount, 2u);
|
||||
EXPECT_EQ(playerA.GetLastFrame().stats.presentedFrameIndex, 21u);
|
||||
EXPECT_EQ(playerB.GetLastFrame().stats.presentedFrameIndex, 21u);
|
||||
}
|
||||
|
||||
TEST(UIRuntimeTest, UISystemUpdateComposesLayersAndRoutesInputToTopInteractiveLayer) {
|
||||
FakeScreenDocumentHost host = {};
|
||||
UIDocumentScreenHost host = {};
|
||||
UISystem system(host);
|
||||
|
||||
UIScreenPlayer& gameplay = system.CreatePlayer();
|
||||
ASSERT_TRUE(gameplay.Load(MakeAsset()));
|
||||
const auto baseLayer = system.PushScreen(
|
||||
BuildScreenAsset(baseView.Path(), "runtime.base"));
|
||||
ASSERT_NE(baseLayer, 0u);
|
||||
|
||||
UIScreenAsset hudAsset = MakeAsset();
|
||||
hudAsset.screenId = "HUD";
|
||||
hudAsset.documentPath = "Assets/UI/Hud.xcui";
|
||||
UIScreenLayerOptions hudOptions = {};
|
||||
hudOptions.debugName = "HUD";
|
||||
hudOptions.acceptsInput = false;
|
||||
UIScreenPlayer& hud = system.CreatePlayer(hudOptions);
|
||||
ASSERT_TRUE(hud.Load(hudAsset));
|
||||
XCEngine::UI::Runtime::UIScreenLayerOptions overlayOptions = {};
|
||||
overlayOptions.debugName = "overlay";
|
||||
overlayOptions.blocksLayersBelow = true;
|
||||
const auto overlayLayer = system.PushScreen(
|
||||
BuildScreenAsset(overlayView.Path(), "runtime.overlay"),
|
||||
overlayOptions);
|
||||
ASSERT_NE(overlayLayer, 0u);
|
||||
|
||||
const UISystemFrameResult& frame = system.Update(MakeFrameInput(33));
|
||||
|
||||
ASSERT_EQ(frame.presentedLayerCount, 2u);
|
||||
EXPECT_EQ(frame.skippedLayerCount, 0u);
|
||||
ASSERT_EQ(frame.layers.size(), 2u);
|
||||
EXPECT_EQ(frame.layers[0].asset.screenId, "MainMenu");
|
||||
EXPECT_EQ(frame.layers[1].asset.screenId, "HUD");
|
||||
EXPECT_EQ(frame.layers[0].stats.inputEventCount, 1u);
|
||||
EXPECT_EQ(frame.layers[1].stats.inputEventCount, 0u);
|
||||
|
||||
ASSERT_EQ(frame.drawData.GetDrawListCount(), 2u);
|
||||
EXPECT_EQ(frame.drawData.GetDrawLists()[0].GetDebugName(), "MainMenu");
|
||||
EXPECT_EQ(frame.drawData.GetDrawLists()[1].GetDebugName(), "HUD");
|
||||
|
||||
ASSERT_EQ(host.buildCalls.size(), 2u);
|
||||
EXPECT_EQ(host.buildCalls[0].displayName, "MainMenu");
|
||||
EXPECT_EQ(host.buildCalls[0].inputEventCount, 1u);
|
||||
EXPECT_EQ(host.buildCalls[1].displayName, "HUD");
|
||||
EXPECT_EQ(host.buildCalls[1].inputEventCount, 0u);
|
||||
}
|
||||
|
||||
TEST(UIRuntimeTest, UISystemModalLayerBlocksLowerLayersAndKeepsOnlyTopFrameVisible) {
|
||||
FakeScreenDocumentHost host = {};
|
||||
UISystem system(host);
|
||||
|
||||
UIScreenAsset gameplayAsset = MakeAsset();
|
||||
gameplayAsset.screenId = "GameplayHUD";
|
||||
gameplayAsset.documentPath = "Assets/UI/GameplayHud.xcui";
|
||||
const UIScreenLayerId gameplayLayer = system.PushScreen(gameplayAsset);
|
||||
ASSERT_NE(gameplayLayer, 0u);
|
||||
|
||||
UIScreenAsset pauseAsset = MakeAsset();
|
||||
pauseAsset.screenId = "PauseMenu";
|
||||
pauseAsset.documentPath = "Assets/UI/PauseMenu.xcui";
|
||||
UIScreenLayerOptions pauseOptions = {};
|
||||
pauseOptions.debugName = "PauseMenu";
|
||||
pauseOptions.blocksLayersBelow = true;
|
||||
const UIScreenLayerId pauseLayer = system.PushScreen(pauseAsset, pauseOptions);
|
||||
ASSERT_NE(pauseLayer, 0u);
|
||||
|
||||
const UISystemFrameResult& frame = system.Update(MakeFrameInput(48));
|
||||
|
||||
EXPECT_EQ(system.GetLayerCount(), 2u);
|
||||
const auto& frame = system.Update(BuildInputState(3u));
|
||||
EXPECT_EQ(frame.presentedLayerCount, 1u);
|
||||
EXPECT_EQ(frame.skippedLayerCount, 1u);
|
||||
ASSERT_EQ(frame.layers.size(), 1u);
|
||||
EXPECT_EQ(frame.layers[0].layerId, pauseLayer);
|
||||
EXPECT_EQ(frame.layers[0].asset.screenId, "PauseMenu");
|
||||
EXPECT_TRUE(frame.layers[0].options.blocksLayersBelow);
|
||||
ASSERT_EQ(frame.drawData.GetDrawListCount(), 1u);
|
||||
EXPECT_EQ(frame.drawData.GetDrawLists()[0].GetDebugName(), "PauseMenu");
|
||||
|
||||
ASSERT_EQ(host.buildCalls.size(), 1u);
|
||||
EXPECT_EQ(host.buildCalls[0].displayName, "PauseMenu");
|
||||
EXPECT_EQ(host.buildCalls[0].inputEventCount, 1u);
|
||||
EXPECT_EQ(frame.layers.size(), 1u);
|
||||
EXPECT_EQ(frame.layers.front().layerId, overlayLayer);
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Overlay Screen"));
|
||||
EXPECT_TRUE(DrawDataContainsText(frame.drawData, "Modal Dialog"));
|
||||
EXPECT_FALSE(DrawDataContainsText(frame.drawData, "Base Screen"));
|
||||
}
|
||||
|
||||
TEST(UIRuntimeTest, UIDocumentScreenHostLoadsRealCompiledDocuments) {
|
||||
const fs::path root = fs::temp_directory_path() / "xcui_runtime_host_load_test";
|
||||
fs::remove_all(root);
|
||||
|
||||
WriteTextFile(
|
||||
root / "RuntimeScreen.xcui",
|
||||
"<View name=\"RuntimeScreen\">\n"
|
||||
" <Column gap=\"10\">\n"
|
||||
" <Text text=\"Runtime HUD\" />\n"
|
||||
" </Column>\n"
|
||||
"</View>\n");
|
||||
WriteTextFile(root / "RuntimeTheme.xctheme", "<Theme name=\"RuntimeTheme\" />\n");
|
||||
|
||||
UIDocumentScreenHost host = {};
|
||||
UIScreenAsset asset = {};
|
||||
asset.screenId = "RuntimeHUD";
|
||||
asset.documentPath = (root / "RuntimeScreen.xcui").string();
|
||||
asset.themePath = (root / "RuntimeTheme.xctheme").string();
|
||||
|
||||
const UIScreenLoadResult loadResult = host.LoadScreen(asset);
|
||||
ASSERT_TRUE(loadResult.succeeded);
|
||||
EXPECT_EQ(loadResult.document.displayName, "RuntimeHUD");
|
||||
EXPECT_TRUE(loadResult.document.viewDocument.valid);
|
||||
EXPECT_TRUE(loadResult.document.hasThemeDocument);
|
||||
EXPECT_FALSE(loadResult.document.dependencies.empty());
|
||||
|
||||
fs::remove_all(root);
|
||||
}
|
||||
|
||||
TEST(UIRuntimeTest, UIDocumentScreenHostBuildsConcreteRuntimeFrame) {
|
||||
const fs::path root = fs::temp_directory_path() / "xcui_runtime_host_frame_test";
|
||||
fs::remove_all(root);
|
||||
|
||||
WriteTextFile(
|
||||
root / "RuntimeScreen.xcui",
|
||||
"<View name=\"RuntimeScreen\" padding=\"18\">\n"
|
||||
" <Column gap=\"10\">\n"
|
||||
" <Text text=\"Runtime HUD\" />\n"
|
||||
" <Card title=\"Quest Tracker\" subtitle=\"2 active objectives\">\n"
|
||||
" <Text text=\"Collect 3 relic shards\" />\n"
|
||||
" </Card>\n"
|
||||
" <Row gap=\"12\">\n"
|
||||
" <Button title=\"Resume\" width=\"stretch\" />\n"
|
||||
" <Button title=\"Settings\" width=\"stretch\" />\n"
|
||||
" </Row>\n"
|
||||
" </Column>\n"
|
||||
"</View>\n");
|
||||
|
||||
UIDocumentScreenHost host = {};
|
||||
UIScreenPlayer player(host);
|
||||
UIScreenAsset asset = {};
|
||||
asset.documentPath = (root / "RuntimeScreen.xcui").string();
|
||||
ASSERT_TRUE(player.Load(asset));
|
||||
|
||||
const UIScreenFrameResult& frame = player.Update(MakeFrameInput(33));
|
||||
EXPECT_TRUE(frame.errorMessage.empty());
|
||||
EXPECT_TRUE(frame.stats.documentLoaded);
|
||||
EXPECT_GT(frame.stats.commandCount, 0u);
|
||||
EXPECT_GT(frame.stats.nodeCount, 0u);
|
||||
EXPECT_GT(frame.stats.filledRectCommandCount, 0u);
|
||||
EXPECT_GT(frame.stats.textCommandCount, 0u);
|
||||
EXPECT_EQ(frame.stats.presentedFrameIndex, 33u);
|
||||
|
||||
fs::remove_all(root);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user