Fix UI document host input integration build
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <XCEngine/UI/Input/UIInputDispatcher.h>
|
||||||
#include <XCEngine/UI/Runtime/UIScreenTypes.h>
|
#include <XCEngine/UI/Runtime/UIScreenTypes.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -22,6 +23,20 @@ public:
|
|||||||
|
|
||||||
class UIDocumentScreenHost final : public IUIScreenDocumentHost {
|
class UIDocumentScreenHost final : public IUIScreenDocumentHost {
|
||||||
public:
|
public:
|
||||||
|
struct InputDebugSnapshot {
|
||||||
|
std::uint64_t totalPointerEventCount = 0u;
|
||||||
|
UIPoint pointerPosition = {};
|
||||||
|
bool pointerInsideViewport = false;
|
||||||
|
std::string hoveredStateKey = {};
|
||||||
|
std::string focusedStateKey = {};
|
||||||
|
std::string activeStateKey = {};
|
||||||
|
std::string captureStateKey = {};
|
||||||
|
std::string lastEventType = {};
|
||||||
|
std::string lastTargetStateKey = {};
|
||||||
|
std::string lastTargetKind = {};
|
||||||
|
std::string lastResult = {};
|
||||||
|
};
|
||||||
|
|
||||||
struct ScrollDebugSnapshot {
|
struct ScrollDebugSnapshot {
|
||||||
std::uint64_t totalWheelEventCount = 0u;
|
std::uint64_t totalWheelEventCount = 0u;
|
||||||
std::uint64_t handledWheelEventCount = 0u;
|
std::uint64_t handledWheelEventCount = 0u;
|
||||||
@@ -42,10 +57,20 @@ public:
|
|||||||
UIScreenFrameResult BuildFrame(
|
UIScreenFrameResult BuildFrame(
|
||||||
const UIScreenDocument& document,
|
const UIScreenDocument& document,
|
||||||
const UIScreenFrameInput& input) override;
|
const UIScreenFrameInput& input) override;
|
||||||
|
const InputDebugSnapshot& GetInputDebugSnapshot() const;
|
||||||
const ScrollDebugSnapshot& GetScrollDebugSnapshot() const;
|
const ScrollDebugSnapshot& GetScrollDebugSnapshot() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct PointerState {
|
||||||
|
UIPoint position = {};
|
||||||
|
bool hasPosition = false;
|
||||||
|
bool insideViewport = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UIInputDispatcher m_inputDispatcher;
|
||||||
std::unordered_map<std::string, float> m_verticalScrollOffsets = {};
|
std::unordered_map<std::string, float> m_verticalScrollOffsets = {};
|
||||||
|
PointerState m_pointerState = {};
|
||||||
|
InputDebugSnapshot m_inputDebugSnapshot = {};
|
||||||
ScrollDebugSnapshot m_scrollDebugSnapshot = {};
|
ScrollDebugSnapshot m_scrollDebugSnapshot = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ constexpr float kHeaderTextGap = 2.0f;
|
|||||||
struct RuntimeLayoutNode {
|
struct RuntimeLayoutNode {
|
||||||
const UIDocumentNode* source = nullptr;
|
const UIDocumentNode* source = nullptr;
|
||||||
std::string stateKey = {};
|
std::string stateKey = {};
|
||||||
|
UIElementId elementId = 0u;
|
||||||
|
UIInputPath inputPath = {};
|
||||||
std::vector<RuntimeLayoutNode> children = {};
|
std::vector<RuntimeLayoutNode> children = {};
|
||||||
UISize desiredSize = {};
|
UISize desiredSize = {};
|
||||||
UISize minimumSize = {};
|
UISize minimumSize = {};
|
||||||
@@ -48,9 +50,19 @@ struct RuntimeLayoutNode {
|
|||||||
UIRect rect = {};
|
UIRect rect = {};
|
||||||
UIRect scrollViewportRect = {};
|
UIRect scrollViewportRect = {};
|
||||||
float scrollOffsetY = 0.0f;
|
float scrollOffsetY = 0.0f;
|
||||||
|
bool pointerInteractive = false;
|
||||||
|
bool focusable = false;
|
||||||
|
bool wantsPointerCapture = false;
|
||||||
bool isScrollView = false;
|
bool isScrollView = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RuntimeNodeVisualState {
|
||||||
|
bool hovered = false;
|
||||||
|
bool focused = false;
|
||||||
|
bool active = false;
|
||||||
|
bool capture = false;
|
||||||
|
};
|
||||||
|
|
||||||
UIColor ToUIColor(const Color& color) {
|
UIColor ToUIColor(const Color& color) {
|
||||||
return UIColor(color.r, color.g, color.b, color.a);
|
return UIColor(color.r, color.g, color.b, color.a);
|
||||||
}
|
}
|
||||||
@@ -59,6 +71,48 @@ std::string ToStdString(const Containers::String& value) {
|
|||||||
return std::string(value.CStr());
|
return std::string(value.CStr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ToLowerAscii(std::string value) {
|
||||||
|
std::transform(
|
||||||
|
value.begin(),
|
||||||
|
value.end(),
|
||||||
|
value.begin(),
|
||||||
|
[](unsigned char ch) {
|
||||||
|
return static_cast<char>(std::tolower(ch));
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryParseBoolString(const std::string& text, bool& outValue) {
|
||||||
|
if (text.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string normalized = ToLowerAscii(text);
|
||||||
|
if (normalized == "true" || normalized == "1" || normalized == "yes" || normalized == "on") {
|
||||||
|
outValue = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized == "false" || normalized == "0" || normalized == "no" || normalized == "off") {
|
||||||
|
outValue = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIElementId HashStateKeyToElementId(const std::string& stateKey) {
|
||||||
|
constexpr UIElementId kOffsetBasis = 14695981039346656037ull;
|
||||||
|
constexpr UIElementId kPrime = 1099511628211ull;
|
||||||
|
|
||||||
|
UIElementId hash = kOffsetBasis;
|
||||||
|
for (unsigned char value : stateKey) {
|
||||||
|
hash ^= static_cast<UIElementId>(value);
|
||||||
|
hash *= kPrime;
|
||||||
|
}
|
||||||
|
return hash == 0u ? 1u : hash;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsUtf8ContinuationByte(unsigned char value) {
|
bool IsUtf8ContinuationByte(unsigned char value) {
|
||||||
return (value & 0xC0u) == 0x80u;
|
return (value & 0xC0u) == 0x80u;
|
||||||
}
|
}
|
||||||
@@ -103,6 +157,14 @@ std::string GetAttribute(
|
|||||||
return attribute != nullptr ? ToStdString(attribute->value) : fallback;
|
return attribute != nullptr ? ToStdString(attribute->value) : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ParseBoolAttribute(
|
||||||
|
const UIDocumentNode& node,
|
||||||
|
const char* name,
|
||||||
|
bool fallback) {
|
||||||
|
bool value = fallback;
|
||||||
|
return TryParseBoolString(GetAttribute(node, name), value) ? value : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
float MeasureHeaderTextWidth(const UIDocumentNode& node) {
|
float MeasureHeaderTextWidth(const UIDocumentNode& node) {
|
||||||
float width = 0.0f;
|
float width = 0.0f;
|
||||||
|
|
||||||
@@ -235,6 +297,10 @@ bool IsScrollViewTag(const std::string& tagName) {
|
|||||||
return tagName == "ScrollView";
|
return tagName == "ScrollView";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsButtonTag(const std::string& tagName) {
|
||||||
|
return tagName == "Button";
|
||||||
|
}
|
||||||
|
|
||||||
bool IsContainerTag(const UIDocumentNode& node) {
|
bool IsContainerTag(const UIDocumentNode& node) {
|
||||||
if (node.children.Size() > 0u) {
|
if (node.children.Size() > 0u) {
|
||||||
return true;
|
return true;
|
||||||
@@ -249,6 +315,24 @@ bool IsContainerTag(const UIDocumentNode& node) {
|
|||||||
tagName == "Button";
|
tagName == "Button";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsPointerInteractiveNode(const UIDocumentNode& node) {
|
||||||
|
const std::string tagName = ToStdString(node.tagName);
|
||||||
|
return ParseBoolAttribute(node, "interactive", IsButtonTag(tagName) || IsScrollViewTag(tagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFocusableNode(const UIDocumentNode& node) {
|
||||||
|
const std::string tagName = ToStdString(node.tagName);
|
||||||
|
return ParseBoolAttribute(node, "focusable", IsButtonTag(tagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WantsPointerCapture(const UIDocumentNode& node) {
|
||||||
|
if (ParseBoolAttribute(node, "capturePointer", false)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToLowerAscii(GetAttribute(node, "capture")) == "pointer";
|
||||||
|
}
|
||||||
|
|
||||||
std::string BuildNodeStateKeySegment(
|
std::string BuildNodeStateKeySegment(
|
||||||
const UIDocumentNode& source,
|
const UIDocumentNode& source,
|
||||||
std::size_t siblingIndex) {
|
std::size_t siblingIndex) {
|
||||||
@@ -280,7 +364,137 @@ bool RectContainsPoint(const UIRect& rect, const UIPoint& point) {
|
|||||||
point.y <= rect.y + rect.height;
|
point.y <= rect.y + rect.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color ResolveBackgroundColor(const UIDocumentNode& node) {
|
bool HasPositiveArea(const UIRect& rect) {
|
||||||
|
return rect.width > 0.0f && rect.height > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIRect IntersectRects(const UIRect& lhs, const UIRect& rhs) {
|
||||||
|
const float left = (std::max)(lhs.x, rhs.x);
|
||||||
|
const float top = (std::max)(lhs.y, rhs.y);
|
||||||
|
const float right = (std::min)(lhs.x + lhs.width, rhs.x + rhs.width);
|
||||||
|
const float bottom = (std::min)(lhs.y + lhs.height, rhs.y + rhs.height);
|
||||||
|
return UIRect(
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
(std::max)(0.0f, right - left),
|
||||||
|
(std::max)(0.0f, bottom - top));
|
||||||
|
}
|
||||||
|
|
||||||
|
const UIRect& GetNodeInteractionRect(const RuntimeLayoutNode& node) {
|
||||||
|
return node.isScrollView ? node.scrollViewportRect : node.rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNodeTargetable(const RuntimeLayoutNode& node) {
|
||||||
|
return node.pointerInteractive || node.focusable || node.isScrollView;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RuntimeLayoutNode* FindNodeByElementId(
|
||||||
|
const RuntimeLayoutNode& node,
|
||||||
|
UIElementId elementId) {
|
||||||
|
if (node.elementId == elementId) {
|
||||||
|
return &node;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const RuntimeLayoutNode& child : node.children) {
|
||||||
|
if (const RuntimeLayoutNode* found = FindNodeByElementId(child, elementId); found != nullptr) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeLayoutNode* FindNodeByElementId(
|
||||||
|
RuntimeLayoutNode& node,
|
||||||
|
UIElementId elementId) {
|
||||||
|
if (node.elementId == elementId) {
|
||||||
|
return &node;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RuntimeLayoutNode& child : node.children) {
|
||||||
|
if (RuntimeLayoutNode* found = FindNodeByElementId(child, elementId); found != nullptr) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PathTargetExists(
|
||||||
|
const RuntimeLayoutNode& root,
|
||||||
|
const UIInputPath& path) {
|
||||||
|
return !path.Empty() && FindNodeByElementId(root, path.Target()) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ResolveStateKeyForPathTarget(
|
||||||
|
const RuntimeLayoutNode& root,
|
||||||
|
const UIInputPath& path) {
|
||||||
|
if (path.Empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const RuntimeLayoutNode* node = FindNodeByElementId(root, path.Target()); node != nullptr) {
|
||||||
|
return node->stateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const RuntimeLayoutNode* FindDeepestInputTarget(
|
||||||
|
const RuntimeLayoutNode& node,
|
||||||
|
const UIPoint& point,
|
||||||
|
const UIRect* clipRect = nullptr) {
|
||||||
|
if (clipRect != nullptr && (!HasPositiveArea(*clipRect) || !RectContainsPoint(*clipRect, point))) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIRect nextClip = clipRect != nullptr ? *clipRect : node.rect;
|
||||||
|
if (!HasPositiveArea(nextClip)) {
|
||||||
|
nextClip = node.rect;
|
||||||
|
}
|
||||||
|
if (node.isScrollView) {
|
||||||
|
nextClip = clipRect != nullptr
|
||||||
|
? IntersectRects(*clipRect, node.scrollViewportRect)
|
||||||
|
: node.scrollViewportRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const RuntimeLayoutNode& child : node.children) {
|
||||||
|
if (const RuntimeLayoutNode* found = FindDeepestInputTarget(child, point, &nextClip); found != nullptr) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const UIRect& targetRect = GetNodeInteractionRect(node);
|
||||||
|
if (!IsNodeTargetable(node) || !HasPositiveArea(targetRect)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipRect != nullptr && !RectContainsPoint(*clipRect, point)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RectContainsPoint(targetRect, point) ? &node : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIInputPath ResolveHoveredPath(
|
||||||
|
const RuntimeLayoutNode& root,
|
||||||
|
const UIPoint& pointerPosition,
|
||||||
|
bool hasPointerPosition,
|
||||||
|
bool pointerInsideViewport) {
|
||||||
|
if (!hasPointerPosition || !pointerInsideViewport) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const RuntimeLayoutNode* hovered = FindDeepestInputTarget(root, pointerPosition); hovered != nullptr) {
|
||||||
|
return hovered->inputPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Color ResolveBackgroundColor(
|
||||||
|
const UIDocumentNode& node,
|
||||||
|
const RuntimeNodeVisualState& state) {
|
||||||
const std::string tone = GetAttribute(node, "tone");
|
const std::string tone = GetAttribute(node, "tone");
|
||||||
const std::string tagName = ToStdString(node.tagName);
|
const std::string tagName = ToStdString(node.tagName);
|
||||||
|
|
||||||
@@ -294,13 +508,33 @@ Color ResolveBackgroundColor(const UIDocumentNode& node) {
|
|||||||
return Color(0.22f, 0.22f, 0.22f, 1.0f);
|
return Color(0.22f, 0.22f, 0.22f, 1.0f);
|
||||||
}
|
}
|
||||||
if (tagName == "Button") {
|
if (tagName == "Button") {
|
||||||
|
if (state.active || state.capture) {
|
||||||
|
return Color(0.30f, 0.30f, 0.30f, 1.0f);
|
||||||
|
}
|
||||||
|
if (state.hovered) {
|
||||||
|
return Color(0.27f, 0.27f, 0.27f, 1.0f);
|
||||||
|
}
|
||||||
return Color(0.24f, 0.24f, 0.24f, 1.0f);
|
return Color(0.24f, 0.24f, 0.24f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Color(0.16f, 0.16f, 0.16f, 1.0f);
|
return Color(0.16f, 0.16f, 0.16f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color ResolveBorderColor(const UIDocumentNode& node) {
|
Color ResolveBorderColor(
|
||||||
|
const UIDocumentNode& node,
|
||||||
|
const RuntimeNodeVisualState& state) {
|
||||||
|
if (state.capture) {
|
||||||
|
return Color(0.82f, 0.82f, 0.82f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.focused || state.active) {
|
||||||
|
return Color(0.62f, 0.62f, 0.62f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.hovered) {
|
||||||
|
return Color(0.45f, 0.45f, 0.45f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
const std::string tone = GetAttribute(node, "tone");
|
const std::string tone = GetAttribute(node, "tone");
|
||||||
if (tone == "accent") {
|
if (tone == "accent") {
|
||||||
return Color(0.42f, 0.42f, 0.42f, 1.0f);
|
return Color(0.42f, 0.42f, 0.42f, 1.0f);
|
||||||
@@ -312,17 +546,46 @@ Color ResolveBorderColor(const UIDocumentNode& node) {
|
|||||||
return Color(0.30f, 0.30f, 0.30f, 1.0f);
|
return Color(0.30f, 0.30f, 0.30f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float ResolveBorderThickness(const RuntimeNodeVisualState& state) {
|
||||||
|
return (state.focused || state.active || state.capture) ? 2.0f : 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPathTarget(
|
||||||
|
const UIInputPath& path,
|
||||||
|
UIElementId elementId) {
|
||||||
|
return !path.Empty() && path.Target() == elementId;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeNodeVisualState ResolveNodeVisualState(
|
||||||
|
const RuntimeLayoutNode& node,
|
||||||
|
const UIInputPath& hoveredPath,
|
||||||
|
const UIFocusController& focusController) {
|
||||||
|
RuntimeNodeVisualState state = {};
|
||||||
|
state.hovered = IsPathTarget(hoveredPath, node.elementId);
|
||||||
|
state.focused = IsPathTarget(focusController.GetFocusedPath(), node.elementId);
|
||||||
|
state.active = IsPathTarget(focusController.GetActivePath(), node.elementId);
|
||||||
|
state.capture = IsPathTarget(focusController.GetPointerCapturePath(), node.elementId);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
RuntimeLayoutNode BuildLayoutTree(
|
RuntimeLayoutNode BuildLayoutTree(
|
||||||
const UIDocumentNode& source,
|
const UIDocumentNode& source,
|
||||||
const std::string& parentStateKey,
|
const std::string& parentStateKey,
|
||||||
|
const UIInputPath& parentInputPath,
|
||||||
std::size_t siblingIndex) {
|
std::size_t siblingIndex) {
|
||||||
RuntimeLayoutNode node = {};
|
RuntimeLayoutNode node = {};
|
||||||
node.source = &source;
|
node.source = &source;
|
||||||
node.stateKey = parentStateKey + "/" + BuildNodeStateKeySegment(source, siblingIndex);
|
node.stateKey = parentStateKey + "/" + BuildNodeStateKeySegment(source, siblingIndex);
|
||||||
|
node.elementId = HashStateKeyToElementId(node.stateKey);
|
||||||
|
node.inputPath = parentInputPath;
|
||||||
|
node.inputPath.elements.push_back(node.elementId);
|
||||||
|
node.pointerInteractive = IsPointerInteractiveNode(source);
|
||||||
|
node.focusable = ParseBoolAttribute(source, "focusable", IsFocusableNode(source));
|
||||||
|
node.wantsPointerCapture = WantsPointerCapture(source);
|
||||||
node.isScrollView = IsScrollViewTag(ToStdString(source.tagName));
|
node.isScrollView = IsScrollViewTag(ToStdString(source.tagName));
|
||||||
node.children.reserve(source.children.Size());
|
node.children.reserve(source.children.Size());
|
||||||
for (std::size_t index = 0; index < source.children.Size(); ++index) {
|
for (std::size_t index = 0; index < source.children.Size(); ++index) {
|
||||||
node.children.push_back(BuildLayoutTree(source.children[index], node.stateKey, index));
|
node.children.push_back(BuildLayoutTree(source.children[index], node.stateKey, node.inputPath, index));
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -534,74 +797,243 @@ const RuntimeLayoutNode* FindFirstScrollView(const RuntimeLayoutNode& node) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ApplyScrollWheelInput(
|
bool ApplyScrollWheelEvent(
|
||||||
RuntimeLayoutNode& root,
|
RuntimeLayoutNode& root,
|
||||||
const UIScreenFrameInput& input,
|
const UIInputEvent& event,
|
||||||
std::unordered_map<std::string, float>& verticalScrollOffsets,
|
std::unordered_map<std::string, float>& verticalScrollOffsets,
|
||||||
UIDocumentScreenHost::ScrollDebugSnapshot& scrollDebugSnapshot) {
|
UIDocumentScreenHost::ScrollDebugSnapshot& scrollDebugSnapshot) {
|
||||||
bool changed = false;
|
if (event.type != UIInputEventType::PointerWheel) {
|
||||||
|
return false;
|
||||||
for (const UIInputEvent& event : input.events) {
|
|
||||||
if (event.type != UIInputEventType::PointerWheel) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
++scrollDebugSnapshot.totalWheelEventCount;
|
|
||||||
scrollDebugSnapshot.lastPointerPosition = event.position;
|
|
||||||
scrollDebugSnapshot.lastWheelDelta = event.wheelDelta;
|
|
||||||
scrollDebugSnapshot.lastTargetStateKey.clear();
|
|
||||||
scrollDebugSnapshot.lastViewportRect = {};
|
|
||||||
scrollDebugSnapshot.lastOverflow = 0.0f;
|
|
||||||
scrollDebugSnapshot.lastOffsetBefore = 0.0f;
|
|
||||||
scrollDebugSnapshot.lastOffsetAfter = 0.0f;
|
|
||||||
scrollDebugSnapshot.lastResult = "No hovered ScrollView";
|
|
||||||
|
|
||||||
RuntimeLayoutNode* hoveredScrollView = FindDeepestHoveredScrollView(root, event.position);
|
|
||||||
if (hoveredScrollView == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollDebugSnapshot.lastTargetStateKey = hoveredScrollView->stateKey;
|
|
||||||
scrollDebugSnapshot.lastViewportRect = hoveredScrollView->scrollViewportRect;
|
|
||||||
scrollDebugSnapshot.lastOverflow = ComputeScrollOverflow(
|
|
||||||
hoveredScrollView->contentDesiredSize.height,
|
|
||||||
hoveredScrollView->scrollViewportRect.height);
|
|
||||||
|
|
||||||
if (scrollDebugSnapshot.lastOverflow <= 0.0f) {
|
|
||||||
scrollDebugSnapshot.lastResult = "Hovered ScrollView has no overflow";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeLayoutNode* target = FindDeepestScrollTarget(root, event.position);
|
|
||||||
if (target == nullptr) {
|
|
||||||
scrollDebugSnapshot.lastResult = "Scroll target resolution failed";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto found = verticalScrollOffsets.find(target->stateKey);
|
|
||||||
const float oldOffset = found != verticalScrollOffsets.end()
|
|
||||||
? found->second
|
|
||||||
: target->scrollOffsetY;
|
|
||||||
scrollDebugSnapshot.lastOffsetBefore = oldOffset;
|
|
||||||
|
|
||||||
const float scrollUnits = event.wheelDelta / 120.0f;
|
|
||||||
const float nextOffset = ClampScrollOffset(
|
|
||||||
oldOffset - scrollUnits * 48.0f,
|
|
||||||
target->contentDesiredSize.height,
|
|
||||||
target->scrollViewportRect.height);
|
|
||||||
scrollDebugSnapshot.lastOffsetAfter = nextOffset;
|
|
||||||
if (std::fabs(nextOffset - oldOffset) <= 0.01f) {
|
|
||||||
scrollDebugSnapshot.lastResult = "Scroll delta clamped to current offset";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
verticalScrollOffsets[target->stateKey] = nextOffset;
|
|
||||||
++scrollDebugSnapshot.handledWheelEventCount;
|
|
||||||
scrollDebugSnapshot.lastResult = "Handled";
|
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return changed;
|
++scrollDebugSnapshot.totalWheelEventCount;
|
||||||
|
scrollDebugSnapshot.lastPointerPosition = event.position;
|
||||||
|
scrollDebugSnapshot.lastWheelDelta = event.wheelDelta;
|
||||||
|
scrollDebugSnapshot.lastTargetStateKey.clear();
|
||||||
|
scrollDebugSnapshot.lastViewportRect = {};
|
||||||
|
scrollDebugSnapshot.lastOverflow = 0.0f;
|
||||||
|
scrollDebugSnapshot.lastOffsetBefore = 0.0f;
|
||||||
|
scrollDebugSnapshot.lastOffsetAfter = 0.0f;
|
||||||
|
scrollDebugSnapshot.lastResult = "No hovered ScrollView";
|
||||||
|
|
||||||
|
RuntimeLayoutNode* hoveredScrollView = FindDeepestHoveredScrollView(root, event.position);
|
||||||
|
if (hoveredScrollView == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollDebugSnapshot.lastTargetStateKey = hoveredScrollView->stateKey;
|
||||||
|
scrollDebugSnapshot.lastViewportRect = hoveredScrollView->scrollViewportRect;
|
||||||
|
scrollDebugSnapshot.lastOverflow = ComputeScrollOverflow(
|
||||||
|
hoveredScrollView->contentDesiredSize.height,
|
||||||
|
hoveredScrollView->scrollViewportRect.height);
|
||||||
|
|
||||||
|
if (scrollDebugSnapshot.lastOverflow <= 0.0f) {
|
||||||
|
scrollDebugSnapshot.lastResult = "Hovered ScrollView has no overflow";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeLayoutNode* target = FindDeepestScrollTarget(root, event.position);
|
||||||
|
if (target == nullptr) {
|
||||||
|
scrollDebugSnapshot.lastResult = "Scroll target resolution failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto found = verticalScrollOffsets.find(target->stateKey);
|
||||||
|
const float oldOffset = found != verticalScrollOffsets.end()
|
||||||
|
? found->second
|
||||||
|
: target->scrollOffsetY;
|
||||||
|
scrollDebugSnapshot.lastOffsetBefore = oldOffset;
|
||||||
|
|
||||||
|
const float scrollUnits = event.wheelDelta / 120.0f;
|
||||||
|
const float nextOffset = ClampScrollOffset(
|
||||||
|
oldOffset - scrollUnits * 48.0f,
|
||||||
|
target->contentDesiredSize.height,
|
||||||
|
target->scrollViewportRect.height);
|
||||||
|
scrollDebugSnapshot.lastOffsetAfter = nextOffset;
|
||||||
|
if (std::fabs(nextOffset - oldOffset) <= 0.01f) {
|
||||||
|
scrollDebugSnapshot.lastResult = "Scroll delta clamped to current offset";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
verticalScrollOffsets[target->stateKey] = nextOffset;
|
||||||
|
++scrollDebugSnapshot.handledWheelEventCount;
|
||||||
|
scrollDebugSnapshot.lastResult = "Handled";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GetInputEventTypeDebugName(UIInputEventType type) {
|
||||||
|
switch (type) {
|
||||||
|
case UIInputEventType::PointerMove:
|
||||||
|
return "PointerMove";
|
||||||
|
case UIInputEventType::PointerEnter:
|
||||||
|
return "PointerEnter";
|
||||||
|
case UIInputEventType::PointerLeave:
|
||||||
|
return "PointerLeave";
|
||||||
|
case UIInputEventType::PointerButtonDown:
|
||||||
|
return "PointerButtonDown";
|
||||||
|
case UIInputEventType::PointerButtonUp:
|
||||||
|
return "PointerButtonUp";
|
||||||
|
case UIInputEventType::PointerWheel:
|
||||||
|
return "PointerWheel";
|
||||||
|
case UIInputEventType::KeyDown:
|
||||||
|
return "KeyDown";
|
||||||
|
case UIInputEventType::KeyUp:
|
||||||
|
return "KeyUp";
|
||||||
|
case UIInputEventType::Character:
|
||||||
|
return "Character";
|
||||||
|
case UIInputEventType::FocusGained:
|
||||||
|
return "FocusGained";
|
||||||
|
case UIInputEventType::FocusLost:
|
||||||
|
return "FocusLost";
|
||||||
|
case UIInputEventType::None:
|
||||||
|
default:
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GetInputTargetKindDebugName(UIInputTargetKind targetKind) {
|
||||||
|
switch (targetKind) {
|
||||||
|
case UIInputTargetKind::Hovered:
|
||||||
|
return "Hovered";
|
||||||
|
case UIInputTargetKind::Focused:
|
||||||
|
return "Focused";
|
||||||
|
case UIInputTargetKind::Captured:
|
||||||
|
return "Captured";
|
||||||
|
case UIInputTargetKind::None:
|
||||||
|
default:
|
||||||
|
return "None";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdatePointerTrackingState(
|
||||||
|
UIPoint& pointerPosition,
|
||||||
|
bool& hasPointerPosition,
|
||||||
|
bool& pointerInsideViewport,
|
||||||
|
const UIInputEvent& event,
|
||||||
|
const UIRect& viewportRect) {
|
||||||
|
switch (event.type) {
|
||||||
|
case UIInputEventType::PointerMove:
|
||||||
|
case UIInputEventType::PointerButtonDown:
|
||||||
|
case UIInputEventType::PointerButtonUp:
|
||||||
|
case UIInputEventType::PointerWheel:
|
||||||
|
case UIInputEventType::PointerEnter:
|
||||||
|
pointerPosition = event.position;
|
||||||
|
hasPointerPosition = true;
|
||||||
|
pointerInsideViewport = RectContainsPoint(viewportRect, event.position);
|
||||||
|
return;
|
||||||
|
case UIInputEventType::PointerLeave:
|
||||||
|
pointerPosition = event.position;
|
||||||
|
hasPointerPosition = true;
|
||||||
|
pointerInsideViewport = false;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SanitizeInputDispatcherState(
|
||||||
|
const RuntimeLayoutNode& root,
|
||||||
|
UIInputDispatcher& inputDispatcher) {
|
||||||
|
UIFocusController& focusController = inputDispatcher.GetFocusController();
|
||||||
|
if (!PathTargetExists(root, focusController.GetFocusedPath())) {
|
||||||
|
focusController.ClearFocus();
|
||||||
|
}
|
||||||
|
if (!PathTargetExists(root, focusController.GetActivePath())) {
|
||||||
|
focusController.ClearActivePath();
|
||||||
|
}
|
||||||
|
if (!PathTargetExists(root, focusController.GetPointerCapturePath())) {
|
||||||
|
focusController.ClearPointerCapturePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateInputDebugSnapshot(
|
||||||
|
const RuntimeLayoutNode& root,
|
||||||
|
const UIInputPath& hoveredPath,
|
||||||
|
const UIInputDispatcher& inputDispatcher,
|
||||||
|
const UIPoint& pointerPosition,
|
||||||
|
bool pointerInsideViewport,
|
||||||
|
UIDocumentScreenHost::InputDebugSnapshot& inputDebugSnapshot) {
|
||||||
|
inputDebugSnapshot.pointerPosition = pointerPosition;
|
||||||
|
inputDebugSnapshot.pointerInsideViewport = pointerInsideViewport;
|
||||||
|
inputDebugSnapshot.hoveredStateKey = ResolveStateKeyForPathTarget(root, hoveredPath);
|
||||||
|
inputDebugSnapshot.focusedStateKey = ResolveStateKeyForPathTarget(
|
||||||
|
root,
|
||||||
|
inputDispatcher.GetFocusController().GetFocusedPath());
|
||||||
|
inputDebugSnapshot.activeStateKey = ResolveStateKeyForPathTarget(
|
||||||
|
root,
|
||||||
|
inputDispatcher.GetFocusController().GetActivePath());
|
||||||
|
inputDebugSnapshot.captureStateKey = ResolveStateKeyForPathTarget(
|
||||||
|
root,
|
||||||
|
inputDispatcher.GetFocusController().GetPointerCapturePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DispatchInputEvent(
|
||||||
|
RuntimeLayoutNode& root,
|
||||||
|
const UIInputEvent& event,
|
||||||
|
const UIInputPath& hoveredPath,
|
||||||
|
UIInputDispatcher& inputDispatcher,
|
||||||
|
UIDocumentScreenHost::InputDebugSnapshot& inputDebugSnapshot) {
|
||||||
|
if (event.type == UIInputEventType::PointerWheel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UIInputRouter::IsPointerEvent(event.type)) {
|
||||||
|
++inputDebugSnapshot.totalPointerEventCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputDebugSnapshot.lastEventType = GetInputEventTypeDebugName(event.type);
|
||||||
|
inputDebugSnapshot.lastTargetKind = "None";
|
||||||
|
inputDebugSnapshot.lastTargetStateKey.clear();
|
||||||
|
inputDebugSnapshot.lastResult = "No target";
|
||||||
|
|
||||||
|
if (event.type == UIInputEventType::FocusLost) {
|
||||||
|
UIFocusController& focusController = inputDispatcher.GetFocusController();
|
||||||
|
focusController.ClearPointerCapturePath();
|
||||||
|
focusController.ClearActivePath();
|
||||||
|
focusController.ClearFocus();
|
||||||
|
inputDebugSnapshot.lastResult = "Focus cleared";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pointerCaptureStarted = false;
|
||||||
|
const UIInputDispatchSummary summary = inputDispatcher.Dispatch(
|
||||||
|
event,
|
||||||
|
hoveredPath,
|
||||||
|
[&](const UIInputDispatchRequest& request) {
|
||||||
|
if (!request.isTargetElement) {
|
||||||
|
return UIInputDispatchDecision{};
|
||||||
|
}
|
||||||
|
|
||||||
|
const RuntimeLayoutNode* node = FindNodeByElementId(root, request.elementId);
|
||||||
|
if (node == nullptr) {
|
||||||
|
return UIInputDispatchDecision{};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type == UIInputEventType::PointerButtonDown &&
|
||||||
|
event.pointerButton == UIPointerButton::Left &&
|
||||||
|
node->wantsPointerCapture) {
|
||||||
|
inputDispatcher.GetFocusController().SetPointerCapturePath(node->inputPath);
|
||||||
|
pointerCaptureStarted = true;
|
||||||
|
return UIInputDispatchDecision{ true, false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return UIInputDispatchDecision{};
|
||||||
|
});
|
||||||
|
|
||||||
|
inputDebugSnapshot.lastTargetKind = GetInputTargetKindDebugName(summary.routing.plan.targetKind);
|
||||||
|
inputDebugSnapshot.lastTargetStateKey = ResolveStateKeyForPathTarget(root, summary.routing.plan.targetPath);
|
||||||
|
inputDebugSnapshot.lastResult = summary.routing.plan.HasTargetPath() ? "Dispatched" : "No target";
|
||||||
|
|
||||||
|
if (pointerCaptureStarted) {
|
||||||
|
inputDebugSnapshot.lastResult = "Pointer capture started";
|
||||||
|
} else if (event.type == UIInputEventType::PointerButtonUp &&
|
||||||
|
event.pointerButton == UIPointerButton::Left &&
|
||||||
|
inputDispatcher.GetFocusController().HasPointerCapture()) {
|
||||||
|
inputDispatcher.GetFocusController().ClearPointerCapturePath();
|
||||||
|
inputDebugSnapshot.lastResult = "Pointer capture cleared";
|
||||||
|
} else if (summary.routing.handled) {
|
||||||
|
inputDebugSnapshot.lastResult = "Handled";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncScrollOffsets(
|
void SyncScrollOffsets(
|
||||||
@@ -623,19 +1055,29 @@ void SyncScrollOffsets(
|
|||||||
|
|
||||||
void EmitNode(
|
void EmitNode(
|
||||||
const RuntimeLayoutNode& node,
|
const RuntimeLayoutNode& node,
|
||||||
|
const UIInputPath& hoveredPath,
|
||||||
|
const UIFocusController& focusController,
|
||||||
UIDrawList& drawList,
|
UIDrawList& drawList,
|
||||||
UIScreenFrameStats& stats) {
|
UIScreenFrameStats& stats) {
|
||||||
const UIDocumentNode& source = *node.source;
|
const UIDocumentNode& source = *node.source;
|
||||||
const std::string tagName = ToStdString(source.tagName);
|
const std::string tagName = ToStdString(source.tagName);
|
||||||
|
const RuntimeNodeVisualState visualState = ResolveNodeVisualState(
|
||||||
|
node,
|
||||||
|
hoveredPath,
|
||||||
|
focusController);
|
||||||
|
|
||||||
++stats.nodeCount;
|
++stats.nodeCount;
|
||||||
|
|
||||||
if (tagName == "View" || tagName == "Card" || tagName == "Button") {
|
if (tagName == "View" || tagName == "Card" || tagName == "Button") {
|
||||||
drawList.AddFilledRect(node.rect, ToUIColor(ResolveBackgroundColor(source)), 10.0f);
|
drawList.AddFilledRect(node.rect, ToUIColor(ResolveBackgroundColor(source, visualState)), 10.0f);
|
||||||
++stats.filledRectCommandCount;
|
++stats.filledRectCommandCount;
|
||||||
|
|
||||||
if (tagName != "View") {
|
if (tagName != "View") {
|
||||||
drawList.AddRectOutline(node.rect, ToUIColor(ResolveBorderColor(source)), 1.0f, 10.0f);
|
drawList.AddRectOutline(
|
||||||
|
node.rect,
|
||||||
|
ToUIColor(ResolveBorderColor(source, visualState)),
|
||||||
|
ResolveBorderThickness(visualState),
|
||||||
|
10.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,7 +1116,9 @@ void EmitNode(
|
|||||||
drawList.AddText(
|
drawList.AddText(
|
||||||
UIPoint(node.rect.x + 12.0f, ComputeCenteredTextTop(node.rect, kButtonFontSize)),
|
UIPoint(node.rect.x + 12.0f, ComputeCenteredTextTop(node.rect, kButtonFontSize)),
|
||||||
ResolveNodeText(source),
|
ResolveNodeText(source),
|
||||||
ToUIColor(Color(0.95f, 0.97f, 1.0f, 1.0f)),
|
ToUIColor(visualState.capture || visualState.focused
|
||||||
|
? Color(1.0f, 1.0f, 1.0f, 1.0f)
|
||||||
|
: Color(0.95f, 0.97f, 1.0f, 1.0f)),
|
||||||
kButtonFontSize);
|
kButtonFontSize);
|
||||||
++stats.textCommandCount;
|
++stats.textCommandCount;
|
||||||
}
|
}
|
||||||
@@ -688,7 +1132,7 @@ void EmitNode(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const RuntimeLayoutNode& child : node.children) {
|
for (const RuntimeLayoutNode& child : node.children) {
|
||||||
EmitNode(child, drawList, stats);
|
EmitNode(child, hoveredPath, focusController, drawList, stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pushScrollClip) {
|
if (pushScrollClip) {
|
||||||
@@ -808,7 +1252,7 @@ UIScreenFrameResult UIDocumentScreenHost::BuildFrame(
|
|||||||
const std::string stateRoot = document.sourcePath.empty()
|
const std::string stateRoot = document.sourcePath.empty()
|
||||||
? document.displayName
|
? document.displayName
|
||||||
: document.sourcePath;
|
: document.sourcePath;
|
||||||
RuntimeLayoutNode root = BuildLayoutTree(document.viewDocument.rootNode, stateRoot, 0u);
|
RuntimeLayoutNode root = BuildLayoutTree(document.viewDocument.rootNode, stateRoot, UIInputPath(), 0u);
|
||||||
MeasureNode(root);
|
MeasureNode(root);
|
||||||
|
|
||||||
UIRect viewportRect = input.viewportRect;
|
UIRect viewportRect = input.viewportRect;
|
||||||
@@ -819,12 +1263,73 @@ UIScreenFrameResult UIDocumentScreenHost::BuildFrame(
|
|||||||
viewportRect.height = (std::max)(360.0f, root.desiredSize.height);
|
viewportRect.height = (std::max)(360.0f, root.desiredSize.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrangeNode(root, viewportRect, m_verticalScrollOffsets);
|
if (!input.focused) {
|
||||||
if (ApplyScrollWheelInput(root, input, m_verticalScrollOffsets, m_scrollDebugSnapshot)) {
|
m_pointerState.insideViewport = false;
|
||||||
ArrangeNode(root, viewportRect, m_verticalScrollOffsets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrangeNode(root, viewportRect, m_verticalScrollOffsets);
|
||||||
|
SanitizeInputDispatcherState(root, m_inputDispatcher);
|
||||||
|
|
||||||
|
UIPoint pointerPosition = m_pointerState.position;
|
||||||
|
bool hasPointerPosition = m_pointerState.hasPosition;
|
||||||
|
bool pointerInsideViewport =
|
||||||
|
input.focused &&
|
||||||
|
m_pointerState.insideViewport &&
|
||||||
|
(!hasPointerPosition || RectContainsPoint(viewportRect, pointerPosition));
|
||||||
|
|
||||||
|
if (!input.focused) {
|
||||||
|
UIFocusController& focusController = m_inputDispatcher.GetFocusController();
|
||||||
|
focusController.ClearPointerCapturePath();
|
||||||
|
focusController.ClearActivePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const UIInputEvent& event : input.events) {
|
||||||
|
UpdatePointerTrackingState(
|
||||||
|
pointerPosition,
|
||||||
|
hasPointerPosition,
|
||||||
|
pointerInsideViewport,
|
||||||
|
event,
|
||||||
|
viewportRect);
|
||||||
|
pointerInsideViewport = input.focused && pointerInsideViewport;
|
||||||
|
|
||||||
|
if (event.type == UIInputEventType::PointerWheel) {
|
||||||
|
m_inputDebugSnapshot.lastEventType = GetInputEventTypeDebugName(event.type);
|
||||||
|
m_inputDebugSnapshot.lastTargetKind = "Hovered";
|
||||||
|
m_inputDebugSnapshot.lastResult = "No hovered ScrollView";
|
||||||
|
if (ApplyScrollWheelEvent(root, event, m_verticalScrollOffsets, m_scrollDebugSnapshot)) {
|
||||||
|
ArrangeNode(root, viewportRect, m_verticalScrollOffsets);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UIInputPath eventHoveredPath = ResolveHoveredPath(
|
||||||
|
root,
|
||||||
|
pointerPosition,
|
||||||
|
hasPointerPosition,
|
||||||
|
pointerInsideViewport);
|
||||||
|
DispatchInputEvent(root, event, eventHoveredPath, m_inputDispatcher, m_inputDebugSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UIInputPath hoveredPath = ResolveHoveredPath(
|
||||||
|
root,
|
||||||
|
pointerPosition,
|
||||||
|
hasPointerPosition,
|
||||||
|
pointerInsideViewport);
|
||||||
|
SanitizeInputDispatcherState(root, m_inputDispatcher);
|
||||||
|
m_pointerState.position = pointerPosition;
|
||||||
|
m_pointerState.hasPosition = hasPointerPosition;
|
||||||
|
m_pointerState.insideViewport = pointerInsideViewport;
|
||||||
|
UpdateInputDebugSnapshot(
|
||||||
|
root,
|
||||||
|
hoveredPath,
|
||||||
|
m_inputDispatcher,
|
||||||
|
pointerPosition,
|
||||||
|
pointerInsideViewport,
|
||||||
|
m_inputDebugSnapshot);
|
||||||
SyncScrollOffsets(root, m_verticalScrollOffsets);
|
SyncScrollOffsets(root, m_verticalScrollOffsets);
|
||||||
|
|
||||||
|
const UIFocusController& focusController = m_inputDispatcher.GetFocusController();
|
||||||
|
|
||||||
if (const RuntimeLayoutNode* primaryScrollView = FindFirstScrollView(root); primaryScrollView != nullptr) {
|
if (const RuntimeLayoutNode* primaryScrollView = FindFirstScrollView(root); primaryScrollView != nullptr) {
|
||||||
m_scrollDebugSnapshot.primaryTargetStateKey = primaryScrollView->stateKey;
|
m_scrollDebugSnapshot.primaryTargetStateKey = primaryScrollView->stateKey;
|
||||||
m_scrollDebugSnapshot.primaryViewportRect = primaryScrollView->scrollViewportRect;
|
m_scrollDebugSnapshot.primaryViewportRect = primaryScrollView->scrollViewportRect;
|
||||||
@@ -838,7 +1343,7 @@ UIScreenFrameResult UIDocumentScreenHost::BuildFrame(
|
|||||||
}
|
}
|
||||||
|
|
||||||
UIDrawList& drawList = result.drawData.EmplaceDrawList(document.displayName);
|
UIDrawList& drawList = result.drawData.EmplaceDrawList(document.displayName);
|
||||||
EmitNode(root, drawList, result.stats);
|
EmitNode(root, hoveredPath, focusController, drawList, result.stats);
|
||||||
|
|
||||||
result.stats.documentLoaded = true;
|
result.stats.documentLoaded = true;
|
||||||
result.stats.drawListCount = result.drawData.GetDrawListCount();
|
result.stats.drawListCount = result.drawData.GetDrawListCount();
|
||||||
@@ -848,6 +1353,10 @@ UIScreenFrameResult UIDocumentScreenHost::BuildFrame(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UIDocumentScreenHost::InputDebugSnapshot& UIDocumentScreenHost::GetInputDebugSnapshot() const {
|
||||||
|
return m_inputDebugSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
const UIDocumentScreenHost::ScrollDebugSnapshot& UIDocumentScreenHost::GetScrollDebugSnapshot() const {
|
const UIDocumentScreenHost::ScrollDebugSnapshot& UIDocumentScreenHost::GetScrollDebugSnapshot() const {
|
||||||
return m_scrollDebugSnapshot;
|
return m_scrollDebugSnapshot;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user