Integrate XCUI runtime context into SceneRuntime
This commit is contained in:
@@ -47,11 +47,12 @@ Current gap:
|
|||||||
- Engine-side runtime ownership is no longer zero: `UIScreenPlayer`, `UIDocumentScreenHost`, and `UISystem` now define a shared runtime contract for loading a screen document, ticking it with input, and collecting `UI::UIDrawData`.
|
- Engine-side runtime ownership is no longer zero: `UIScreenPlayer`, `UIDocumentScreenHost`, and `UISystem` now define a shared runtime contract for loading a screen document, ticking it with input, and collecting `UI::UIDrawData`.
|
||||||
- `UISystem` now supports layered screen composition semantics: stacked screen players, top-interactive input routing, and modal layers that block lower screens.
|
- `UISystem` now supports layered screen composition semantics: stacked screen players, top-interactive input routing, and modal layers that block lower screens.
|
||||||
- `UIScreenStackController::ReplaceTop` now preserves the previous top screen if the replacement screen fails to load, so runtime menu flows do not silently drop their active layer on bad assets.
|
- `UIScreenStackController::ReplaceTop` now preserves the previous top screen if the replacement screen fails to load, so runtime menu flows do not silently drop their active layer on bad assets.
|
||||||
|
- `SceneRuntime` now owns a dedicated `UISceneRuntimeContext`, so game/runtime code has a first-class place to configure viewport/focus, queue `UIInputEvent`s, drive `UISystem` each `Update`, and inspect the latest UI frame result.
|
||||||
- Runtime screen emission now also carries concrete button text in the shared document host path instead of silently dropping button labels.
|
- Runtime screen emission now also carries concrete button text in the shared document host path instead of silently dropping button labels.
|
||||||
|
|
||||||
Current gap:
|
Current gap:
|
||||||
|
|
||||||
- No production game-loop integration has been wired yet from scene/runtime systems into `UISystem`.
|
- Runtime UI is now wired into `SceneRuntime`, but render submission is still limited to producing `UIDrawData`; there is no game-view/runtime presenter path that automatically draws those frames yet.
|
||||||
- The runtime widget library is still shallow and missing the editor-grade controls that will later be shared downward.
|
- The runtime widget library is still shallow and missing the editor-grade controls that will later be shared downward.
|
||||||
|
|
||||||
### 3. Editor Layer
|
### 3. Editor Layer
|
||||||
@@ -88,6 +89,7 @@ Current gap:
|
|||||||
- `new_editor_xcui_hosted_preview_presenter_tests`: `12/12`
|
- `new_editor_xcui_hosted_preview_presenter_tests`: `12/12`
|
||||||
- `XCNewEditor` Debug target builds successfully
|
- `XCNewEditor` Debug target builds successfully
|
||||||
- `core_ui_tests`: `28/28`
|
- `core_ui_tests`: `28/28`
|
||||||
|
- `scene_tests`: `65/65`
|
||||||
- `core_ui_style_tests`: `5/5`
|
- `core_ui_style_tests`: `5/5`
|
||||||
- `ui_resource_tests`: `11/11`
|
- `ui_resource_tests`: `11/11`
|
||||||
- `editor_tests` targeted bridge smoke: `3/3`
|
- `editor_tests` targeted bridge smoke: `3/3`
|
||||||
@@ -119,11 +121,21 @@ Current gap:
|
|||||||
- Engine runtime layer added:
|
- Engine runtime layer added:
|
||||||
- `UIScreenPlayer`
|
- `UIScreenPlayer`
|
||||||
- `UIDocumentScreenHost`
|
- `UIDocumentScreenHost`
|
||||||
|
- `UISceneRuntimeContext`
|
||||||
- `UIScreenStackController`
|
- `UIScreenStackController`
|
||||||
- `UISystem`
|
- `UISystem`
|
||||||
- layered screen composition and modal blocking semantics
|
- layered screen composition and modal blocking semantics
|
||||||
- Runtime/game integration scaffolding now includes reusable `HUD/menu/modal` stack helpers on top of `UISystem`.
|
- Runtime/game integration scaffolding now includes reusable `HUD/menu/modal` stack helpers on top of `UISystem`.
|
||||||
- `UIScreenStackController` replacement now rolls back safely on failure instead of popping the active top layer first.
|
- `UIScreenStackController` replacement now rolls back safely on failure instead of popping the active top layer first.
|
||||||
|
- `SceneRuntime` now exposes XCUI runtime ownership directly:
|
||||||
|
- `GetUISystem()`
|
||||||
|
- `GetUIScreenStackController()`
|
||||||
|
- `GetLastUIFrame()`
|
||||||
|
- `SetUIViewportRect(...)`
|
||||||
|
- `SetUIFocused(...)`
|
||||||
|
- `QueueUIInputEvent(...)`
|
||||||
|
- `ClearQueuedUIInputEvents()`
|
||||||
|
- automatic `UISystem` ticking during `SceneRuntime::Update(...)`
|
||||||
- Runtime document-host draw emission now preserves button labels for shared screen rendering.
|
- Runtime document-host draw emission now preserves button labels for shared screen rendering.
|
||||||
- RHI image path improvements:
|
- RHI image path improvements:
|
||||||
- clipped image UV adjustment
|
- clipped image UV adjustment
|
||||||
@@ -162,7 +174,7 @@ Current gap:
|
|||||||
|
|
||||||
## Next Phase
|
## Next Phase
|
||||||
|
|
||||||
1. Expand runtime/game-layer ownership from the current document host + layered `UISystem` into reusable menu/HUD stack patterns and engine runtime integration.
|
1. Expand runtime/game-layer ownership from the current `SceneRuntime` UI context into scene-declared HUD/menu bootstrapping, draw submission, and higher-level runtime UI policies.
|
||||||
2. Promote the current editor-facing widget prototypes out of authored `LayoutLab` content and into reusable XCUI widget/runtime modules, then continue with toolbar/menu and more native shell-owned chrome.
|
2. Promote the current editor-facing widget prototypes out of authored `LayoutLab` content and into reusable XCUI widget/runtime modules, then continue with toolbar/menu and more native shell-owned chrome.
|
||||||
3. Add a native XCUI host compositor on the existing window-level compositor seam so `new_editor` can present without going through ImGui-owned draw data.
|
3. Add a native XCUI host compositor on the existing window-level compositor seam so `new_editor` can present without going through ImGui-owned draw data.
|
||||||
4. Reduce remaining ImGui leakage in hosted preview surfaces and panel contracts now that the compositor seam is in place.
|
4. Reduce remaining ImGui leakage in hosted preview surfaces and panel contracts now that the compositor seam is in place.
|
||||||
|
|||||||
@@ -525,9 +525,11 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenTypes.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenTypes.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenDocumentHost.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenDocumentHost.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenPlayer.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenPlayer.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UISceneRuntimeContext.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UISystem.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UISystem.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UIScreenDocumentHost.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UIScreenDocumentHost.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UIScreenPlayer.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UIScreenPlayer.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UISceneRuntimeContext.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UIScreenStackController.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UIScreenStackController.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UISystem.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Runtime/UISystem.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,37 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <XCEngine/Scene/Scene.h>
|
#include <XCEngine/Scene/Scene.h>
|
||||||
|
#include <XCEngine/UI/Types.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace UI {
|
||||||
|
namespace Runtime {
|
||||||
|
|
||||||
|
class UISceneRuntimeContext;
|
||||||
|
class UISystem;
|
||||||
|
class UIScreenStackController;
|
||||||
|
struct UISystemFrameResult;
|
||||||
|
|
||||||
|
} // namespace Runtime
|
||||||
|
} // namespace UI
|
||||||
|
} // namespace XCEngine
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Components {
|
namespace Components {
|
||||||
|
|
||||||
class SceneRuntime {
|
class SceneRuntime {
|
||||||
public:
|
public:
|
||||||
|
SceneRuntime();
|
||||||
|
~SceneRuntime();
|
||||||
|
|
||||||
|
SceneRuntime(SceneRuntime&& other) noexcept;
|
||||||
|
SceneRuntime& operator=(SceneRuntime&& other) noexcept;
|
||||||
|
|
||||||
|
SceneRuntime(const SceneRuntime&) = delete;
|
||||||
|
SceneRuntime& operator=(const SceneRuntime&) = delete;
|
||||||
|
|
||||||
void Start(Scene* scene);
|
void Start(Scene* scene);
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
@@ -14,10 +39,19 @@ public:
|
|||||||
void Update(float deltaTime);
|
void Update(float deltaTime);
|
||||||
void LateUpdate(float deltaTime);
|
void LateUpdate(float deltaTime);
|
||||||
|
|
||||||
|
UI::Runtime::UISystem& GetUISystem();
|
||||||
|
UI::Runtime::UIScreenStackController& GetUIScreenStackController();
|
||||||
|
const UI::Runtime::UISystemFrameResult& GetLastUIFrame() const;
|
||||||
|
void SetUIViewportRect(const UI::UIRect& viewportRect);
|
||||||
|
void SetUIFocused(bool focused);
|
||||||
|
void QueueUIInputEvent(const UI::UIInputEvent& event);
|
||||||
|
void ClearQueuedUIInputEvents();
|
||||||
|
|
||||||
bool IsRunning() const { return m_running; }
|
bool IsRunning() const { return m_running; }
|
||||||
Scene* GetScene() const { return m_scene; }
|
Scene* GetScene() const { return m_scene; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::unique_ptr<UI::Runtime::UISceneRuntimeContext> m_uiRuntime;
|
||||||
Scene* m_scene = nullptr;
|
Scene* m_scene = nullptr;
|
||||||
bool m_running = false;
|
bool m_running = false;
|
||||||
};
|
};
|
||||||
|
|||||||
40
engine/include/XCEngine/UI/Runtime/UISceneRuntimeContext.h
Normal file
40
engine/include/XCEngine/UI/Runtime/UISceneRuntimeContext.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/UI/Runtime/UIScreenDocumentHost.h>
|
||||||
|
#include <XCEngine/UI/Runtime/UIScreenStackController.h>
|
||||||
|
#include <XCEngine/UI/Runtime/UISystem.h>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace UI {
|
||||||
|
namespace Runtime {
|
||||||
|
|
||||||
|
class UISceneRuntimeContext {
|
||||||
|
public:
|
||||||
|
UISceneRuntimeContext();
|
||||||
|
|
||||||
|
UISystem& GetSystem();
|
||||||
|
const UISystem& GetSystem() const;
|
||||||
|
|
||||||
|
UIScreenStackController& GetStackController();
|
||||||
|
const UIScreenStackController& GetStackController() const;
|
||||||
|
|
||||||
|
const UISystemFrameResult& GetLastFrame() const;
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
void SetViewportRect(const UIRect& viewportRect);
|
||||||
|
void SetFocused(bool focused);
|
||||||
|
void QueueInputEvent(const UIInputEvent& event);
|
||||||
|
void ClearQueuedInputEvents();
|
||||||
|
void Update(double deltaTimeSeconds);
|
||||||
|
|
||||||
|
private:
|
||||||
|
UIDocumentScreenHost m_documentHost = {};
|
||||||
|
UISystem m_system;
|
||||||
|
UIScreenStackController m_stackController;
|
||||||
|
UIScreenFrameInput m_pendingFrameInput = {};
|
||||||
|
std::uint64_t m_nextFrameIndex = 1u;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Runtime
|
||||||
|
} // namespace UI
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -1,10 +1,21 @@
|
|||||||
#include "Scene/SceneRuntime.h"
|
#include "Scene/SceneRuntime.h"
|
||||||
|
|
||||||
#include "Scripting/ScriptEngine.h"
|
#include "Scripting/ScriptEngine.h"
|
||||||
|
#include <XCEngine/UI/Runtime/UISceneRuntimeContext.h>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Components {
|
namespace Components {
|
||||||
|
|
||||||
|
SceneRuntime::SceneRuntime()
|
||||||
|
: m_uiRuntime(std::make_unique<UI::Runtime::UISceneRuntimeContext>()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneRuntime::~SceneRuntime() = default;
|
||||||
|
|
||||||
|
SceneRuntime::SceneRuntime(SceneRuntime&& other) noexcept = default;
|
||||||
|
|
||||||
|
SceneRuntime& SceneRuntime::operator=(SceneRuntime&& other) noexcept = default;
|
||||||
|
|
||||||
void SceneRuntime::Start(Scene* scene) {
|
void SceneRuntime::Start(Scene* scene) {
|
||||||
if (m_running && m_scene == scene) {
|
if (m_running && m_scene == scene) {
|
||||||
return;
|
return;
|
||||||
@@ -18,16 +29,19 @@ void SceneRuntime::Start(Scene* scene) {
|
|||||||
|
|
||||||
m_scene = scene;
|
m_scene = scene;
|
||||||
m_running = true;
|
m_running = true;
|
||||||
|
m_uiRuntime->Reset();
|
||||||
Scripting::ScriptEngine::Get().OnRuntimeStart(scene);
|
Scripting::ScriptEngine::Get().OnRuntimeStart(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneRuntime::Stop() {
|
void SceneRuntime::Stop() {
|
||||||
if (!m_running) {
|
if (!m_running) {
|
||||||
|
m_uiRuntime->Reset();
|
||||||
m_scene = nullptr;
|
m_scene = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scripting::ScriptEngine::Get().OnRuntimeStop();
|
Scripting::ScriptEngine::Get().OnRuntimeStop();
|
||||||
|
m_uiRuntime->Reset();
|
||||||
m_running = false;
|
m_running = false;
|
||||||
m_scene = nullptr;
|
m_scene = nullptr;
|
||||||
}
|
}
|
||||||
@@ -49,6 +63,7 @@ void SceneRuntime::Update(float deltaTime) {
|
|||||||
|
|
||||||
Scripting::ScriptEngine::Get().OnUpdate(deltaTime);
|
Scripting::ScriptEngine::Get().OnUpdate(deltaTime);
|
||||||
m_scene->Update(deltaTime);
|
m_scene->Update(deltaTime);
|
||||||
|
m_uiRuntime->Update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneRuntime::LateUpdate(float deltaTime) {
|
void SceneRuntime::LateUpdate(float deltaTime) {
|
||||||
@@ -60,5 +75,33 @@ void SceneRuntime::LateUpdate(float deltaTime) {
|
|||||||
m_scene->LateUpdate(deltaTime);
|
m_scene->LateUpdate(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UI::Runtime::UISystem& SceneRuntime::GetUISystem() {
|
||||||
|
return m_uiRuntime->GetSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
UI::Runtime::UIScreenStackController& SceneRuntime::GetUIScreenStackController() {
|
||||||
|
return m_uiRuntime->GetStackController();
|
||||||
|
}
|
||||||
|
|
||||||
|
const UI::Runtime::UISystemFrameResult& SceneRuntime::GetLastUIFrame() const {
|
||||||
|
return m_uiRuntime->GetLastFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneRuntime::SetUIViewportRect(const UI::UIRect& viewportRect) {
|
||||||
|
m_uiRuntime->SetViewportRect(viewportRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneRuntime::SetUIFocused(bool focused) {
|
||||||
|
m_uiRuntime->SetFocused(focused);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneRuntime::QueueUIInputEvent(const UI::UIInputEvent& event) {
|
||||||
|
m_uiRuntime->QueueInputEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneRuntime::ClearQueuedUIInputEvents() {
|
||||||
|
m_uiRuntime->ClearQueuedInputEvents();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Components
|
} // namespace Components
|
||||||
} // namespace XCEngine
|
} // namespace XCEngine
|
||||||
|
|||||||
64
engine/src/UI/Runtime/UISceneRuntimeContext.cpp
Normal file
64
engine/src/UI/Runtime/UISceneRuntimeContext.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#include <XCEngine/UI/Runtime/UISceneRuntimeContext.h>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace UI {
|
||||||
|
namespace Runtime {
|
||||||
|
|
||||||
|
UISceneRuntimeContext::UISceneRuntimeContext()
|
||||||
|
: m_system(m_documentHost)
|
||||||
|
, m_stackController(m_system) {
|
||||||
|
}
|
||||||
|
|
||||||
|
UISystem& UISceneRuntimeContext::GetSystem() {
|
||||||
|
return m_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UISystem& UISceneRuntimeContext::GetSystem() const {
|
||||||
|
return m_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIScreenStackController& UISceneRuntimeContext::GetStackController() {
|
||||||
|
return m_stackController;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UIScreenStackController& UISceneRuntimeContext::GetStackController() const {
|
||||||
|
return m_stackController;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UISystemFrameResult& UISceneRuntimeContext::GetLastFrame() const {
|
||||||
|
return m_system.GetLastFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UISceneRuntimeContext::Reset() {
|
||||||
|
m_stackController.Clear();
|
||||||
|
m_system.DestroyAllPlayers();
|
||||||
|
m_pendingFrameInput = {};
|
||||||
|
m_nextFrameIndex = 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UISceneRuntimeContext::SetViewportRect(const UIRect& viewportRect) {
|
||||||
|
m_pendingFrameInput.viewportRect = viewportRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UISceneRuntimeContext::SetFocused(bool focused) {
|
||||||
|
m_pendingFrameInput.focused = focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UISceneRuntimeContext::QueueInputEvent(const UIInputEvent& event) {
|
||||||
|
m_pendingFrameInput.events.push_back(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UISceneRuntimeContext::ClearQueuedInputEvents() {
|
||||||
|
m_pendingFrameInput.events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UISceneRuntimeContext::Update(double deltaTimeSeconds) {
|
||||||
|
m_pendingFrameInput.deltaTimeSeconds = deltaTimeSeconds;
|
||||||
|
m_pendingFrameInput.frameIndex = m_nextFrameIndex++;
|
||||||
|
m_system.Update(m_pendingFrameInput);
|
||||||
|
m_pendingFrameInput.events.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Runtime
|
||||||
|
} // namespace UI
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -4,13 +4,22 @@
|
|||||||
#include <XCEngine/Scripting/IScriptRuntime.h>
|
#include <XCEngine/Scripting/IScriptRuntime.h>
|
||||||
#include <XCEngine/Scripting/ScriptComponent.h>
|
#include <XCEngine/Scripting/ScriptComponent.h>
|
||||||
#include <XCEngine/Scripting/ScriptEngine.h>
|
#include <XCEngine/Scripting/ScriptEngine.h>
|
||||||
|
#include <XCEngine/UI/Runtime/UIScreenStackController.h>
|
||||||
|
#include <XCEngine/UI/Runtime/UIScreenTypes.h>
|
||||||
|
#include <XCEngine/UI/Runtime/UISystem.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using namespace XCEngine::Components;
|
using namespace XCEngine::Components;
|
||||||
using namespace XCEngine::Scripting;
|
using namespace XCEngine::Scripting;
|
||||||
|
using XCEngine::UI::Runtime::UIScreenAsset;
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -67,6 +76,61 @@ private:
|
|||||||
std::vector<std::string>* m_events = nullptr;
|
std::vector<std::string>* m_events = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TempFileScope {
|
||||||
|
public:
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
~TempFileScope() {
|
||||||
|
std::error_code ec;
|
||||||
|
fs::remove(m_path, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fs::path& Path() const {
|
||||||
|
return m_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
fs::path m_path = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string BuildViewMarkup(const char* heroTitle) {
|
||||||
|
return
|
||||||
|
"<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"
|
||||||
|
" </Column>\n"
|
||||||
|
"</View>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
class RecordingScriptRuntime : public IScriptRuntime {
|
class RecordingScriptRuntime : public IScriptRuntime {
|
||||||
public:
|
public:
|
||||||
explicit RecordingScriptRuntime(std::vector<std::string>* events)
|
explicit RecordingScriptRuntime(std::vector<std::string>* events)
|
||||||
@@ -304,4 +368,66 @@ TEST_F(SceneRuntimeTest, StartingNewSceneStopsPreviousRuntimeFirst) {
|
|||||||
runtime.Stop();
|
runtime.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneRuntimeTest, UpdateTicksUiRuntimeAndClearsQueuedInputEvents) {
|
||||||
|
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||||
|
runtime.Start(runtimeScene);
|
||||||
|
runtime.SetUIViewportRect(XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f));
|
||||||
|
runtime.SetUIFocused(true);
|
||||||
|
|
||||||
|
TempFileScope menuView("xcui_scene_runtime_menu", ".xcui", BuildViewMarkup("Runtime Menu"));
|
||||||
|
const auto layerId = runtime.GetUIScreenStackController().PushMenu(
|
||||||
|
BuildScreenAsset(menuView.Path(), "runtime.menu"),
|
||||||
|
"menu");
|
||||||
|
ASSERT_NE(layerId, 0u);
|
||||||
|
|
||||||
|
XCEngine::UI::UIInputEvent textEvent = {};
|
||||||
|
textEvent.type = XCEngine::UI::UIInputEventType::Character;
|
||||||
|
textEvent.character = 'A';
|
||||||
|
runtime.QueueUIInputEvent(textEvent);
|
||||||
|
|
||||||
|
runtime.Update(0.016f);
|
||||||
|
|
||||||
|
const auto& firstFrame = runtime.GetLastUIFrame();
|
||||||
|
ASSERT_EQ(firstFrame.presentedLayerCount, 1u);
|
||||||
|
ASSERT_EQ(firstFrame.layers.size(), 1u);
|
||||||
|
EXPECT_EQ(firstFrame.frameIndex, 1u);
|
||||||
|
EXPECT_EQ(firstFrame.layers.front().stats.inputEventCount, 1u);
|
||||||
|
EXPECT_TRUE(DrawDataContainsText(firstFrame.drawData, "Runtime Menu"));
|
||||||
|
|
||||||
|
runtime.Update(0.016f);
|
||||||
|
|
||||||
|
const auto& secondFrame = runtime.GetLastUIFrame();
|
||||||
|
ASSERT_EQ(secondFrame.presentedLayerCount, 1u);
|
||||||
|
ASSERT_EQ(secondFrame.layers.size(), 1u);
|
||||||
|
EXPECT_EQ(secondFrame.frameIndex, 2u);
|
||||||
|
EXPECT_EQ(secondFrame.layers.front().stats.inputEventCount, 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(SceneRuntimeTest, StopClearsUiRuntimeState) {
|
||||||
|
Scene* runtimeScene = CreateScene("RuntimeScene");
|
||||||
|
runtime.Start(runtimeScene);
|
||||||
|
runtime.SetUIViewportRect(XCEngine::UI::UIRect(0.0f, 0.0f, 800.0f, 480.0f));
|
||||||
|
runtime.SetUIFocused(true);
|
||||||
|
|
||||||
|
TempFileScope menuView("xcui_scene_runtime_pause", ".xcui", BuildViewMarkup("Pause Menu"));
|
||||||
|
const auto layerId = runtime.GetUIScreenStackController().PushMenu(
|
||||||
|
BuildScreenAsset(menuView.Path(), "runtime.pause"),
|
||||||
|
"pause");
|
||||||
|
ASSERT_NE(layerId, 0u);
|
||||||
|
|
||||||
|
runtime.Update(0.016f);
|
||||||
|
ASSERT_EQ(runtime.GetUISystem().GetLayerCount(), 1u);
|
||||||
|
ASSERT_EQ(runtime.GetUIScreenStackController().GetEntryCount(), 1u);
|
||||||
|
ASSERT_EQ(runtime.GetLastUIFrame().presentedLayerCount, 1u);
|
||||||
|
|
||||||
|
runtime.Stop();
|
||||||
|
|
||||||
|
EXPECT_FALSE(runtime.IsRunning());
|
||||||
|
EXPECT_EQ(runtime.GetScene(), nullptr);
|
||||||
|
EXPECT_EQ(runtime.GetUISystem().GetLayerCount(), 0u);
|
||||||
|
EXPECT_EQ(runtime.GetUIScreenStackController().GetEntryCount(), 0u);
|
||||||
|
EXPECT_EQ(runtime.GetLastUIFrame().presentedLayerCount, 0u);
|
||||||
|
EXPECT_TRUE(runtime.GetLastUIFrame().layers.empty());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user