Add native scrolling to new editor tree views

This commit is contained in:
2026-04-21 01:38:55 +08:00
parent 383d1e9c73
commit 33c88f8234
7 changed files with 236 additions and 38 deletions

View File

@@ -27,6 +27,26 @@ float ResolveTreeViewRowHeight(
return item.desiredHeight > 0.0f ? item.desiredHeight : metrics.rowHeight;
}
float ResolveContentHeight(
const std::vector<std::size_t>& visibleItemIndices,
const std::vector<UIEditorTreeViewItem>& items,
const UIEditorTreeViewMetrics& metrics) {
float contentHeight = 0.0f;
for (std::size_t visibleOffset = 0u; visibleOffset < visibleItemIndices.size(); ++visibleOffset) {
const std::size_t itemIndex = visibleItemIndices[visibleOffset];
if (itemIndex >= items.size()) {
continue;
}
contentHeight += ResolveTreeViewRowHeight(items[itemIndex], metrics);
if (visibleOffset + 1u < visibleItemIndices.size()) {
contentHeight += metrics.rowGap;
}
}
return ClampNonNegative(contentHeight);
}
float ResolveTextTop(
const ::XCEngine::UI::UIRect& rect,
float fontSize,
@@ -174,11 +194,21 @@ std::size_t FindUIEditorTreeViewFirstVisibleChildItemIndex(
: UIEditorTreeViewInvalidIndex;
}
float MeasureUIEditorTreeViewContentHeight(
const std::vector<UIEditorTreeViewItem>& items,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorTreeViewMetrics& metrics) {
const std::vector<std::size_t> visibleItemIndices =
CollectUIEditorTreeViewVisibleItemIndices(items, expansionModel);
return ResolveContentHeight(visibleItemIndices, items, metrics);
}
UIEditorTreeViewLayout BuildUIEditorTreeViewLayout(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorTreeViewItem>& items,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorTreeViewMetrics& metrics) {
const UIEditorTreeViewMetrics& metrics,
float verticalOffset) {
UIEditorTreeViewLayout layout = {};
layout.bounds = ::XCEngine::UI::UIRect(
bounds.x,
@@ -186,6 +216,11 @@ UIEditorTreeViewLayout BuildUIEditorTreeViewLayout(
ClampNonNegative(bounds.width),
ClampNonNegative(bounds.height));
layout.visibleItemIndices = CollectUIEditorTreeViewVisibleItemIndices(items, expansionModel);
layout.scrollViewLayout = BuildUIEditorScrollViewLayout(
layout.bounds,
ResolveContentHeight(layout.visibleItemIndices, items, metrics),
verticalOffset,
metrics.scrollViewMetrics);
layout.rowRects.reserve(layout.visibleItemIndices.size());
layout.disclosureRects.reserve(layout.visibleItemIndices.size());
layout.iconRects.reserve(layout.visibleItemIndices.size());
@@ -193,7 +228,9 @@ UIEditorTreeViewLayout BuildUIEditorTreeViewLayout(
layout.itemHasChildren.reserve(layout.visibleItemIndices.size());
layout.itemExpanded.reserve(layout.visibleItemIndices.size());
float rowY = layout.bounds.y;
const ::XCEngine::UI::UIPoint contentOrigin =
ResolveUIEditorScrollViewContentOrigin(layout.scrollViewLayout);
float rowY = contentOrigin.y;
for (std::size_t visibleOffset = 0u;
visibleOffset < layout.visibleItemIndices.size();
++visibleOffset) {
@@ -204,9 +241,9 @@ UIEditorTreeViewLayout BuildUIEditorTreeViewLayout(
const bool expanded = hasChildren && expansionModel.IsExpanded(item.itemId);
const ::XCEngine::UI::UIRect rowRect(
layout.bounds.x,
layout.scrollViewLayout.contentRect.x,
rowY,
layout.bounds.width,
layout.scrollViewLayout.contentRect.width,
rowHeight);
const float contentX =
rowRect.x + metrics.horizontalPadding + static_cast<float>(item.depth) * metrics.indentWidth;
@@ -230,9 +267,11 @@ UIEditorTreeViewLayout BuildUIEditorTreeViewLayout(
const ::XCEngine::UI::UIRect labelRect(
labelStartX,
rowRect.y,
(rowRect.x + rowRect.width) -
labelStartX -
metrics.horizontalPadding,
(std::max)(
(rowRect.x + rowRect.width) -
labelStartX -
metrics.horizontalPadding,
0.0f),
rowRect.height);
layout.rowRects.push_back(rowRect);
@@ -278,13 +317,16 @@ void AppendUIEditorTreeViewBackground(
const UIEditorTreeViewState& state,
const UIEditorTreeViewPalette& palette,
const UIEditorTreeViewMetrics& metrics) {
drawList.AddFilledRect(layout.bounds, palette.surfaceColor, metrics.cornerRounding);
drawList.AddRectOutline(
layout.bounds,
state.focused ? palette.focusedBorderColor : palette.borderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.cornerRounding);
UIEditorScrollViewState scrollViewState = state.scrollViewState;
scrollViewState.focused = state.focused;
AppendUIEditorScrollViewBackground(
drawList,
layout.scrollViewLayout,
scrollViewState,
palette.scrollViewPalette,
metrics.scrollViewMetrics);
drawList.PushClipRect(layout.scrollViewLayout.contentRect);
for (std::size_t visibleOffset = 0u; visibleOffset < layout.rowRects.size(); ++visibleOffset) {
const UIEditorTreeViewItem& item = items[layout.visibleItemIndices[visibleOffset]];
const bool selected = selectionModel.IsSelected(item.itemId);
@@ -299,6 +341,7 @@ void AppendUIEditorTreeViewBackground(
: palette.rowHoverColor;
drawList.AddFilledRect(layout.rowRects[visibleOffset], rowColor, metrics.cornerRounding);
}
drawList.PopClipRect();
}
void AppendUIEditorTreeViewForeground(
@@ -307,7 +350,7 @@ void AppendUIEditorTreeViewForeground(
const std::vector<UIEditorTreeViewItem>& items,
const UIEditorTreeViewPalette& palette,
const UIEditorTreeViewMetrics& metrics) {
drawList.PushClipRect(layout.bounds);
drawList.PushClipRect(layout.scrollViewLayout.contentRect);
for (std::size_t visibleOffset = 0u; visibleOffset < layout.rowRects.size(); ++visibleOffset) {
const UIEditorTreeViewItem& item = items[layout.visibleItemIndices[visibleOffset]];
if (layout.itemHasChildren[visibleOffset]) {
@@ -344,7 +387,7 @@ void AppendUIEditorTreeView(
const UIEditorTreeViewPalette& palette,
const UIEditorTreeViewMetrics& metrics) {
const UIEditorTreeViewLayout layout =
BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics);
BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics, 0.0f);
AppendUIEditorTreeViewBackground(
drawList,
layout,