engine: sync editor rendering and ui changes

This commit is contained in:
2026-04-08 16:09:15 +08:00
parent 31756847ab
commit 162f1cc12e
153 changed files with 4454 additions and 2990 deletions

View File

@@ -7,6 +7,7 @@
#include <XCEngine/UI/Layout/LayoutEngine.h>
#include <XCEngine/UI/Layout/UITabStripLayout.h>
#include <XCEngine/UI/Style/DocumentStyleCompiler.h>
#include <XCEngine/UI/Widgets/UIScrollModel.h>
#include <XCEngine/UI/Widgets/UITabStripModel.h>
#include <algorithm>
@@ -985,14 +986,6 @@ std::string BuildNodeStateKeySegment(
return ToStdString(source.tagName) + "#" + std::to_string(siblingIndex);
}
float ComputeScrollOverflow(float contentExtent, float viewportExtent) {
return (std::max)(0.0f, contentExtent - viewportExtent);
}
float ClampScrollOffset(float offset, float contentExtent, float viewportExtent) {
return (std::max)(0.0f, (std::min)(offset, ComputeScrollOverflow(contentExtent, viewportExtent)));
}
bool RectContainsPoint(const UIRect& rect, const UIPoint& point) {
return point.x >= rect.x &&
point.x <= rect.x + rect.width &&
@@ -1885,7 +1878,7 @@ void ArrangeNode(
const auto found = verticalScrollOffsets.find(node.stateKey);
const float requestedOffset = found != verticalScrollOffsets.end() ? found->second : 0.0f;
node.scrollViewportRect = contentRect;
node.scrollOffsetY = ClampScrollOffset(
node.scrollOffsetY = Widgets::ClampUIScrollOffset(
requestedOffset,
node.contentDesiredSize.height,
contentRect.height);
@@ -1932,7 +1925,7 @@ RuntimeLayoutNode* FindDeepestScrollTarget(
return nullptr;
}
if (ComputeScrollOverflow(node.contentDesiredSize.height, node.scrollViewportRect.height) <= 0.0f) {
if (Widgets::ComputeUIScrollOverflow(node.contentDesiredSize.height, node.scrollViewportRect.height) <= 0.0f) {
return nullptr;
}
@@ -2001,7 +1994,7 @@ bool ApplyScrollWheelEvent(
scrollDebugSnapshot.lastTargetStateKey = hoveredScrollView->stateKey;
scrollDebugSnapshot.lastViewportRect = hoveredScrollView->scrollViewportRect;
scrollDebugSnapshot.lastOverflow = ComputeScrollOverflow(
scrollDebugSnapshot.lastOverflow = Widgets::ComputeUIScrollOverflow(
hoveredScrollView->contentDesiredSize.height,
hoveredScrollView->scrollViewportRect.height);
@@ -2022,18 +2015,18 @@ bool ApplyScrollWheelEvent(
: target->scrollOffsetY;
scrollDebugSnapshot.lastOffsetBefore = oldOffset;
const float scrollUnits = event.wheelDelta / 120.0f;
const float nextOffset = ClampScrollOffset(
oldOffset - scrollUnits * 48.0f,
const Widgets::UIScrollWheelResult scrollResult = Widgets::ApplyUIScrollWheel(
oldOffset,
event.wheelDelta,
target->contentDesiredSize.height,
target->scrollViewportRect.height);
scrollDebugSnapshot.lastOffsetAfter = nextOffset;
if (std::fabs(nextOffset - oldOffset) <= 0.01f) {
scrollDebugSnapshot.lastOffsetAfter = scrollResult.offsetAfter;
if (!scrollResult.changed) {
scrollDebugSnapshot.lastResult = "Scroll delta clamped to current offset";
return false;
}
verticalScrollOffsets[target->stateKey] = nextOffset;
verticalScrollOffsets[target->stateKey] = scrollResult.offsetAfter;
++scrollDebugSnapshot.handledWheelEventCount;
scrollDebugSnapshot.lastResult = "Handled";
return true;
@@ -2545,7 +2538,8 @@ void SyncScrollOffsets(
const RuntimeLayoutNode& node,
std::unordered_map<std::string, float>& verticalScrollOffsets) {
if (node.isScrollView) {
const float overflow = ComputeScrollOverflow(node.contentDesiredSize.height, node.scrollViewportRect.height);
const float overflow =
Widgets::ComputeUIScrollOverflow(node.contentDesiredSize.height, node.scrollViewportRect.height);
if (overflow <= 0.0f || node.scrollOffsetY <= 0.0f) {
verticalScrollOffsets.erase(node.stateKey);
} else {
@@ -3009,7 +3003,7 @@ UIScreenFrameResult UIDocumentScreenHost::BuildFrame(
if (const RuntimeLayoutNode* primaryScrollView = FindFirstScrollView(root); primaryScrollView != nullptr) {
m_scrollDebugSnapshot.primaryTargetStateKey = primaryScrollView->stateKey;
m_scrollDebugSnapshot.primaryViewportRect = primaryScrollView->scrollViewportRect;
m_scrollDebugSnapshot.primaryOverflow = ComputeScrollOverflow(
m_scrollDebugSnapshot.primaryOverflow = Widgets::ComputeUIScrollOverflow(
primaryScrollView->contentDesiredSize.height,
primaryScrollView->scrollViewportRect.height);
} else {

View File

@@ -0,0 +1,67 @@
#include <XCEngine/UI/Widgets/UIScrollModel.h>
#include <algorithm>
#include <cmath>
namespace XCEngine {
namespace UI {
namespace Widgets {
float ComputeUIScrollOverflow(float contentExtent, float viewportExtent) {
return (std::max)(0.0f, contentExtent - viewportExtent);
}
float ClampUIScrollOffset(float offset, float contentExtent, float viewportExtent) {
return (std::max)(0.0f, (std::min)(offset, ComputeUIScrollOverflow(contentExtent, viewportExtent)));
}
UIScrollWheelResult ApplyUIScrollWheel(
float offset,
float wheelDelta,
float contentExtent,
float viewportExtent,
float wheelStep,
float epsilon) {
UIScrollWheelResult result = {};
result.offsetBefore = ClampUIScrollOffset(offset, contentExtent, viewportExtent);
result.overflow = ComputeUIScrollOverflow(contentExtent, viewportExtent);
result.offsetAfter = result.offsetBefore;
if (result.overflow <= 0.0f || std::fabs(wheelDelta) <= epsilon || wheelStep <= 0.0f) {
return result;
}
const float scrollUnits = wheelDelta / 120.0f;
result.offsetAfter = ClampUIScrollOffset(
result.offsetBefore - scrollUnits * wheelStep,
contentExtent,
viewportExtent);
result.changed = std::fabs(result.offsetAfter - result.offsetBefore) > epsilon;
return result;
}
float EnsureUIScrollOffsetVisible(
float offset,
float itemStart,
float itemExtent,
float contentExtent,
float viewportExtent) {
const float clampedOffset = ClampUIScrollOffset(offset, contentExtent, viewportExtent);
if (viewportExtent <= 0.0f || itemExtent <= 0.0f) {
return clampedOffset;
}
const float itemEnd = itemStart + itemExtent;
const float viewportEnd = clampedOffset + viewportExtent;
float nextOffset = clampedOffset;
if (itemStart < clampedOffset) {
nextOffset = itemStart;
} else if (itemEnd > viewportEnd) {
nextOffset = itemEnd - viewportExtent;
}
return ClampUIScrollOffset(nextOffset, contentExtent, viewportExtent);
}
} // namespace Widgets
} // namespace UI
} // namespace XCEngine