Add XCUI core scroll view validation in new_editor
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <unordered_set>
|
||||
@@ -22,6 +23,9 @@ namespace {
|
||||
using ::XCEngine::UI::UIColor;
|
||||
using ::XCEngine::UI::UIDrawData;
|
||||
using ::XCEngine::UI::UIDrawList;
|
||||
using ::XCEngine::UI::UIInputEvent;
|
||||
using ::XCEngine::UI::UIInputEventType;
|
||||
using ::XCEngine::UI::UIInputModifiers;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
using ::XCEngine::UI::UIRect;
|
||||
using ::XCEngine::UI::Runtime::UIScreenFrameInput;
|
||||
@@ -53,6 +57,35 @@ std::string TruncateText(const std::string& text, std::size_t maxLength) {
|
||||
return text.substr(0, maxLength - 3u) + "...";
|
||||
}
|
||||
|
||||
std::string FormatFloat(float value) {
|
||||
std::ostringstream stream;
|
||||
stream.setf(std::ios::fixed, std::ios::floatfield);
|
||||
stream.precision(1);
|
||||
stream << value;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string FormatPoint(const UIPoint& point) {
|
||||
return "(" + FormatFloat(point.x) + ", " + FormatFloat(point.y) + ")";
|
||||
}
|
||||
|
||||
std::string FormatRect(const UIRect& rect) {
|
||||
return "(" + FormatFloat(rect.x) +
|
||||
", " + FormatFloat(rect.y) +
|
||||
", " + FormatFloat(rect.width) +
|
||||
", " + FormatFloat(rect.height) +
|
||||
")";
|
||||
}
|
||||
|
||||
UIInputModifiers BuildInputModifiers(size_t wParam) {
|
||||
UIInputModifiers modifiers = {};
|
||||
modifiers.shift = (wParam & MK_SHIFT) != 0;
|
||||
modifiers.control = (wParam & MK_CONTROL) != 0;
|
||||
modifiers.alt = (GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||
modifiers.super = (GetKeyState(VK_LWIN) & 0x8000) != 0 || (GetKeyState(VK_RWIN) & 0x8000) != 0;
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Application::Application()
|
||||
@@ -170,11 +203,14 @@ void Application::RenderFrame() {
|
||||
m_lastFrameTime = now;
|
||||
|
||||
RefreshStructuredScreen();
|
||||
std::vector<UIInputEvent> frameEvents = std::move(m_pendingInputEvents);
|
||||
m_pendingInputEvents.clear();
|
||||
|
||||
UIDrawData drawData = {};
|
||||
if (m_useStructuredScreen && m_screenPlayer.IsLoaded()) {
|
||||
UIScreenFrameInput input = {};
|
||||
input.viewportRect = UIRect(0.0f, 0.0f, width, height);
|
||||
input.events = std::move(frameEvents);
|
||||
input.deltaTimeSeconds = deltaTimeSeconds;
|
||||
input.frameIndex = ++m_frameIndex;
|
||||
input.focused = GetForegroundWindow() == m_hwnd;
|
||||
@@ -219,6 +255,26 @@ void Application::OnResize(UINT width, UINT height) {
|
||||
m_renderer.Resize(width, height);
|
||||
}
|
||||
|
||||
void Application::QueuePointerWheelEvent(short wheelDelta, WPARAM wParam, LPARAM lParam) {
|
||||
if (m_hwnd == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
POINT screenPoint = {
|
||||
GET_X_LPARAM(lParam),
|
||||
GET_Y_LPARAM(lParam)
|
||||
};
|
||||
ScreenToClient(m_hwnd, &screenPoint);
|
||||
|
||||
UIInputEvent event = {};
|
||||
event.type = UIInputEventType::PointerWheel;
|
||||
event.position = UIPoint(static_cast<float>(screenPoint.x), static_cast<float>(screenPoint.y));
|
||||
event.wheelDelta = static_cast<float>(wheelDelta);
|
||||
event.modifiers = BuildInputModifiers(static_cast<size_t>(wParam));
|
||||
m_pendingInputEvents.push_back(event);
|
||||
m_autoScreenshot.RequestCapture("wheel");
|
||||
}
|
||||
|
||||
bool Application::LoadStructuredScreen(const char* triggerReason) {
|
||||
m_screenAsset = {};
|
||||
m_screenAsset.screenId = "new_editor.editor_shell";
|
||||
@@ -316,13 +372,58 @@ bool Application::DetectTrackedFileChange() const {
|
||||
|
||||
void Application::AppendRuntimeOverlay(UIDrawData& drawData, float width, float height) const {
|
||||
const bool authoredMode = m_useStructuredScreen && m_screenPlayer.IsLoaded();
|
||||
const float panelWidth = authoredMode ? 256.0f : 360.0f;
|
||||
const float panelWidth = authoredMode ? 420.0f : 360.0f;
|
||||
std::vector<std::string> detailLines = {};
|
||||
detailLines.push_back(
|
||||
authoredMode
|
||||
? "Hot reload watches authored UI resources."
|
||||
: "Using native fallback while authored UI is invalid.");
|
||||
|
||||
if (authoredMode) {
|
||||
const auto& scrollDebug = m_documentHost.GetScrollDebugSnapshot();
|
||||
if (!scrollDebug.primaryTargetStateKey.empty()) {
|
||||
detailLines.push_back(
|
||||
"Primary: " +
|
||||
TruncateText(scrollDebug.primaryTargetStateKey, 52u));
|
||||
detailLines.push_back(
|
||||
"Primary viewport: " +
|
||||
FormatRect(scrollDebug.primaryViewportRect));
|
||||
detailLines.push_back(
|
||||
"Primary overflow: " +
|
||||
FormatFloat(scrollDebug.primaryOverflow));
|
||||
}
|
||||
detailLines.push_back(
|
||||
"Wheel total/handled: " +
|
||||
std::to_string(scrollDebug.totalWheelEventCount) +
|
||||
" / " +
|
||||
std::to_string(scrollDebug.handledWheelEventCount));
|
||||
if (scrollDebug.totalWheelEventCount > 0u) {
|
||||
detailLines.push_back(
|
||||
"Last wheel " +
|
||||
FormatFloat(scrollDebug.lastWheelDelta) +
|
||||
" at " +
|
||||
FormatPoint(scrollDebug.lastPointerPosition));
|
||||
detailLines.push_back(
|
||||
"Result: " +
|
||||
(scrollDebug.lastResult.empty() ? std::string("n/a") : scrollDebug.lastResult));
|
||||
if (!scrollDebug.lastTargetStateKey.empty()) {
|
||||
detailLines.push_back(
|
||||
"Target: " +
|
||||
TruncateText(scrollDebug.lastTargetStateKey, 52u));
|
||||
detailLines.push_back(
|
||||
"Viewport: " +
|
||||
FormatRect(scrollDebug.lastViewportRect));
|
||||
detailLines.push_back(
|
||||
"Overflow/offset: " +
|
||||
FormatFloat(scrollDebug.lastOverflow) +
|
||||
" | " +
|
||||
FormatFloat(scrollDebug.lastOffsetBefore) +
|
||||
" -> " +
|
||||
FormatFloat(scrollDebug.lastOffsetAfter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_autoScreenshot.HasPendingCapture()) {
|
||||
detailLines.push_back("Shot pending...");
|
||||
} else if (!m_autoScreenshot.GetLastCaptureSummary().empty()) {
|
||||
@@ -393,6 +494,12 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEWHEEL:
|
||||
if (application != nullptr) {
|
||||
application->QueuePointerWheelEvent(GET_WHEEL_DELTA_WPARAM(wParam), wParam, lParam);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_ERASEBKGND:
|
||||
return 1;
|
||||
case WM_DESTROY:
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <XCEngine/UI/Runtime/UIScreenPlayer.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
@@ -40,6 +41,7 @@ private:
|
||||
void Shutdown();
|
||||
void RenderFrame();
|
||||
void OnResize(UINT width, UINT height);
|
||||
void QueuePointerWheelEvent(short wheelDelta, WPARAM wParam, LPARAM lParam);
|
||||
bool LoadStructuredScreen(const char* triggerReason);
|
||||
void RefreshStructuredScreen();
|
||||
void RebuildTrackedFileStates();
|
||||
@@ -60,6 +62,7 @@ private:
|
||||
std::chrono::steady_clock::time_point m_lastFrameTime = {};
|
||||
std::chrono::steady_clock::time_point m_lastReloadPollTime = {};
|
||||
std::uint64_t m_frameIndex = 0;
|
||||
std::vector<::XCEngine::UI::UIInputEvent> m_pendingInputEvents = {};
|
||||
bool m_useStructuredScreen = false;
|
||||
std::string m_runtimeStatus = {};
|
||||
std::string m_runtimeError = {};
|
||||
|
||||
@@ -46,13 +46,23 @@
|
||||
</Column>
|
||||
</Card>
|
||||
|
||||
<Card title="Console" subtitle="runtime smoke log" height="220">
|
||||
<Column gap="8">
|
||||
<Text text="Info XCUI authored screen loaded." />
|
||||
<Text text="Info Theme + schema resources are tracked for reload." />
|
||||
<Text text="Warn Viewport host stays out of scope in this phase." />
|
||||
<Text text="Todo Replace shell placeholders with Editor widgets." />
|
||||
</Column>
|
||||
<Card title="Console" subtitle="wheel-scroll core validation" height="220">
|
||||
<ScrollView id="console-scroll" height="fill">
|
||||
<Column gap="8">
|
||||
<Text text="Scroll here with the mouse wheel." />
|
||||
<Text text="Check that content clips cleanly inside this card." />
|
||||
<Text text="Info XCUI authored screen loaded." />
|
||||
<Text text="Info Theme + schema resources are tracked for reload." />
|
||||
<Text text="Warn Viewport host stays out of scope in this phase." />
|
||||
<Text text="Todo Replace shell placeholders with Editor widgets." />
|
||||
<Text text="Trace ScrollView should retain offset across frames." />
|
||||
<Text text="Trace Wheel input should only affect the hovered view." />
|
||||
<Text text="Trace Hidden rows must stay clipped below the footer." />
|
||||
<Text text="Trace Resize should not corrupt the scroll offset clamp." />
|
||||
<Text text="Trace Hot reload should rebuild layout without tearing." />
|
||||
<Text text="Trace This panel exists only to validate XCUI core scroll." />
|
||||
</Column>
|
||||
</ScrollView>
|
||||
</Card>
|
||||
</Column>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user