From 33c88f823420b8e5e0d23eaa76eec3fd142e9f82 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 21 Apr 2026 01:38:55 +0800 Subject: [PATCH] Add native scrolling to new editor tree views --- .../app/Features/Hierarchy/HierarchyPanel.cpp | 6 +- .../app/Features/Project/ProjectPanel.cpp | 12 +- .../XCEditor/Collections/UIEditorTreeView.h | 14 +- .../Collections/UIEditorTreeViewInteraction.h | 3 + .../src/Collections/UIEditorTreeView.cpp | 73 +++++++-- .../UIEditorTreeViewInteraction.cpp | 153 ++++++++++++++++-- new_editor/src/Foundation/UIEditorTheme.cpp | 13 ++ 7 files changed, 236 insertions(+), 38 deletions(-) diff --git a/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp b/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp index 57a395f3..c765a1e5 100644 --- a/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp +++ b/new_editor/app/Features/Hierarchy/HierarchyPanel.cpp @@ -569,7 +569,8 @@ void HierarchyPanel::ProcessDragAndFrameEvents( bounds, m_treeItems, m_expansion, - ResolveUIEditorTreeViewMetrics()); + ResolveUIEditorTreeViewMetrics(), + m_treeInteractionState.verticalOffset); EmitReparentEvent( dragResult.droppedToRoot ? EventKind::MovedToRoot : EventKind::Reparented, dragResult.draggedItemId, @@ -622,7 +623,8 @@ void HierarchyPanel::Update( panelState->bounds, m_treeItems, m_expansion, - treeMetrics); + treeMetrics, + m_treeInteractionState.verticalOffset); if (m_renameState.active || !m_pendingRenameItemId.empty()) { m_treeFrame.layout = layout; diff --git a/new_editor/app/Features/Project/ProjectPanel.cpp b/new_editor/app/Features/Project/ProjectPanel.cpp index 389f7bff..8c3bc863 100644 --- a/new_editor/app/Features/Project/ProjectPanel.cpp +++ b/new_editor/app/Features/Project/ProjectPanel.cpp @@ -1101,7 +1101,8 @@ std::vector ProjectPanel::BuildTreeInteractionInputEvents( m_layout.treeRect, GetBrowserModel().GetTreeItems(), m_folderExpansion, - ResolveUIEditorTreeViewMetrics()); + ResolveUIEditorTreeViewMetrics(), + m_treeInteractionState.verticalOffset); return BuildUIEditorTreePanelInteractionInputEvents( m_treeDragState, layout, @@ -1548,7 +1549,8 @@ void ProjectPanel::Update( m_layout.treeRect, GetBrowserModel().GetTreeItems(), m_folderExpansion, - treeMetrics); + treeMetrics, + m_treeInteractionState.verticalOffset); m_treeFrame.result = {}; if ((m_renameState.active || !m_pendingRenameItemId.empty()) && @@ -1673,7 +1675,8 @@ void ProjectPanel::Update( m_layout.treeRect, GetBrowserModel().GetTreeItems(), m_folderExpansion, - treeMetrics); + treeMetrics, + m_treeInteractionState.verticalOffset); } struct ProjectAssetDragCallbacks { @@ -1795,7 +1798,8 @@ void ProjectPanel::Update( m_layout.treeRect, GetBrowserModel().GetTreeItems(), m_folderExpansion, - treeMetrics); + treeMetrics, + m_treeInteractionState.verticalOffset); } const bool suppressPanelPointerEvents = diff --git a/new_editor/include/XCEditor/Collections/UIEditorTreeView.h b/new_editor/include/XCEditor/Collections/UIEditorTreeView.h index 55f73dd4..cbbc492c 100644 --- a/new_editor/include/XCEditor/Collections/UIEditorTreeView.h +++ b/new_editor/include/XCEditor/Collections/UIEditorTreeView.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -32,6 +34,7 @@ struct UIEditorTreeViewItem { struct UIEditorTreeViewState { std::string hoveredItemId = {}; bool focused = false; + UIEditorScrollViewState scrollViewState = {}; }; struct UIEditorTreeViewMetrics { @@ -48,6 +51,7 @@ struct UIEditorTreeViewMetrics { float cornerRounding = 6.0f; float borderThickness = 1.0f; float focusedBorderThickness = 2.0f; + UIEditorScrollViewMetrics scrollViewMetrics = {}; }; struct UIEditorTreeViewPalette { @@ -67,10 +71,12 @@ struct UIEditorTreeViewPalette { ::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f); ::XCEngine::UI::UIColor textColor = ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f); + UIEditorScrollViewPalette scrollViewPalette = {}; }; struct UIEditorTreeViewLayout { ::XCEngine::UI::UIRect bounds = {}; + UIEditorScrollViewLayout scrollViewLayout = {}; std::vector visibleItemIndices = {}; std::vector<::XCEngine::UI::UIRect> rowRects = {}; std::vector<::XCEngine::UI::UIRect> disclosureRects = {}; @@ -111,11 +117,17 @@ std::size_t FindUIEditorTreeViewFirstVisibleChildItemIndex( const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, std::size_t itemIndex); +float MeasureUIEditorTreeViewContentHeight( + const std::vector& items, + const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, + const UIEditorTreeViewMetrics& metrics = {}); + UIEditorTreeViewLayout BuildUIEditorTreeViewLayout( const ::XCEngine::UI::UIRect& bounds, const std::vector& items, const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, - const UIEditorTreeViewMetrics& metrics = {}); + const UIEditorTreeViewMetrics& metrics = {}, + float verticalOffset = 0.0f); UIEditorTreeViewHitTarget HitTestUIEditorTreeView( const UIEditorTreeViewLayout& layout, diff --git a/new_editor/include/XCEditor/Collections/UIEditorTreeViewInteraction.h b/new_editor/include/XCEditor/Collections/UIEditorTreeViewInteraction.h index 034ab796..5005c45e 100644 --- a/new_editor/include/XCEditor/Collections/UIEditorTreeViewInteraction.h +++ b/new_editor/include/XCEditor/Collections/UIEditorTreeViewInteraction.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -14,9 +15,11 @@ namespace XCEngine::UI::Editor { struct UIEditorTreeViewInteractionState { Widgets::UIEditorTreeViewState treeViewState = {}; + UIEditorScrollViewInteractionState scrollViewInteractionState = {}; ::XCEngine::UI::Widgets::UIKeyboardNavigationModel keyboardNavigation = {}; std::string selectionAnchorId = {}; ::XCEngine::UI::UIPoint pointerPosition = {}; + float verticalOffset = 0.0f; bool hasPointerPosition = false; }; diff --git a/new_editor/src/Collections/UIEditorTreeView.cpp b/new_editor/src/Collections/UIEditorTreeView.cpp index 54307277..ac5c7639 100644 --- a/new_editor/src/Collections/UIEditorTreeView.cpp +++ b/new_editor/src/Collections/UIEditorTreeView.cpp @@ -27,6 +27,26 @@ float ResolveTreeViewRowHeight( return item.desiredHeight > 0.0f ? item.desiredHeight : metrics.rowHeight; } +float ResolveContentHeight( + const std::vector& visibleItemIndices, + const std::vector& 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& items, + const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, + const UIEditorTreeViewMetrics& metrics) { + const std::vector visibleItemIndices = + CollectUIEditorTreeViewVisibleItemIndices(items, expansionModel); + return ResolveContentHeight(visibleItemIndices, items, metrics); +} + UIEditorTreeViewLayout BuildUIEditorTreeViewLayout( const ::XCEngine::UI::UIRect& bounds, const std::vector& 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(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& 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, diff --git a/new_editor/src/Collections/UIEditorTreeViewInteraction.cpp b/new_editor/src/Collections/UIEditorTreeViewInteraction.cpp index 476aec20..3a6697c0 100644 --- a/new_editor/src/Collections/UIEditorTreeViewInteraction.cpp +++ b/new_editor/src/Collections/UIEditorTreeViewInteraction.cpp @@ -2,6 +2,7 @@ #include +#include #include namespace XCEngine::UI::Editor { @@ -12,23 +13,48 @@ using ::XCEngine::Input::KeyCode; using ::XCEngine::UI::UIInputEvent; using ::XCEngine::UI::UIInputEventType; using ::XCEngine::UI::UIPointerButton; -using Widgets::BuildUIEditorTreeViewLayout; +using ::XCEngine::UI::Widgets::UISelectionModel; using Widgets::DoesUIEditorTreeViewItemHaveChildren; using Widgets::FindUIEditorTreeViewFirstVisibleChildItemIndex; using Widgets::FindUIEditorTreeViewItemIndex; using Widgets::FindUIEditorTreeViewParentItemIndex; using Widgets::HitTestUIEditorTreeView; using Widgets::IsUIEditorTreeViewPointInside; +using Widgets::MeasureUIEditorTreeViewContentHeight; using Widgets::UIEditorTreeViewHitTarget; using Widgets::UIEditorTreeViewHitTargetKind; using Widgets::UIEditorTreeViewInvalidIndex; +Widgets::UIEditorTreeViewLayout BuildLayout( + UIEditorTreeViewInteractionState& state, + const ::XCEngine::UI::UIRect& bounds, + const std::vector& items, + const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, + const Widgets::UIEditorTreeViewMetrics& metrics) { + Widgets::UIEditorTreeViewLayout layout = + Widgets::BuildUIEditorTreeViewLayout( + bounds, + items, + expansionModel, + metrics, + state.verticalOffset); + state.verticalOffset = layout.scrollViewLayout.verticalOffset; + return layout; +} + +void SyncScrollState(UIEditorTreeViewInteractionState& state) { + state.treeViewState.scrollViewState = + state.scrollViewInteractionState.scrollViewState; + state.treeViewState.scrollViewState.focused = state.treeViewState.focused; +} + bool ShouldUsePointerPosition(const UIInputEvent& event) { switch (event.type) { case UIInputEventType::PointerMove: case UIInputEventType::PointerEnter: case UIInputEventType::PointerButtonDown: case UIInputEventType::PointerButtonUp: + case UIInputEventType::PointerWheel: return true; default: return false; @@ -81,7 +107,7 @@ std::size_t FindVisibleIndexForItemId( void SyncKeyboardNavigation( UIEditorTreeViewInteractionState& state, - const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, + const UISelectionModel& selectionModel, const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items) { state.keyboardNavigation.SetItemCount(layout.visibleItemIndices.size()); @@ -105,7 +131,7 @@ void SyncKeyboardNavigation( void SyncSelectionAnchor( UIEditorTreeViewInteractionState& state, - const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel) { + const UISelectionModel& selectionModel) { if (!selectionModel.HasSelection()) { state.selectionAnchorId.clear(); return; @@ -137,7 +163,7 @@ bool IsDescendantItem( bool SelectVisibleItem( UIEditorTreeViewInteractionState& state, - ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, + UISelectionModel& selectionModel, const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items, std::size_t visibleIndex, @@ -165,7 +191,7 @@ bool SelectVisibleItem( bool ToggleVisibleItemSelection( UIEditorTreeViewInteractionState& state, - ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, + UISelectionModel& selectionModel, const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items, std::size_t visibleIndex, @@ -191,7 +217,7 @@ bool ToggleVisibleItemSelection( bool SelectVisibleItemRange( UIEditorTreeViewInteractionState& state, - ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, + UISelectionModel& selectionModel, const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items, std::size_t visibleIndex, @@ -262,7 +288,7 @@ void PopulateCurrentVisibleItemResult( bool ApplyVerticalNavigation( UIEditorTreeViewInteractionState& state, - ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, + UISelectionModel& selectionModel, const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items, std::int32_t keyCode, @@ -311,7 +337,7 @@ bool ApplyVerticalNavigation( bool ApplyHorizontalNavigation( UIEditorTreeViewInteractionState& state, - ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, + UISelectionModel& selectionModel, ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items, @@ -396,7 +422,7 @@ bool ApplyHorizontalNavigation( void NormalizeSelectionAfterCollapse( UIEditorTreeViewInteractionState& state, - ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, + UISelectionModel& selectionModel, const Widgets::UIEditorTreeViewLayout& layout, const std::vector& items, std::string_view collapsedItemId, @@ -429,24 +455,98 @@ void NormalizeSelectionAfterCollapse( false); } +float ResolveVisibleOffset( + const Widgets::UIEditorTreeViewLayout& layout, + std::size_t visibleIndex, + const Widgets::UIEditorTreeViewMetrics& metrics) { + if (visibleIndex >= layout.rowRects.size()) { + return layout.scrollViewLayout.verticalOffset; + } + + const ::XCEngine::UI::UIRect& rowRect = layout.rowRects[visibleIndex]; + const ::XCEngine::UI::UIRect& contentRect = layout.scrollViewLayout.contentRect; + const float rowBottom = rowRect.y + rowRect.height; + const float contentBottom = contentRect.y + contentRect.height; + float verticalOffset = layout.scrollViewLayout.verticalOffset; + + if (rowRect.y < contentRect.y) { + verticalOffset -= contentRect.y - rowRect.y; + } else if (rowBottom > contentBottom) { + verticalOffset += rowBottom - contentBottom; + } + + return Widgets::ClampUIEditorScrollViewOffset( + layout.bounds, + layout.scrollViewLayout.contentHeight, + verticalOffset, + metrics.scrollViewMetrics); +} + +void EnsureVisibleSelection( + UIEditorTreeViewInteractionState& state, + UISelectionModel& selectionModel, + const std::vector& items, + const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, + const ::XCEngine::UI::UIRect& bounds, + Widgets::UIEditorTreeViewLayout& layout, + UIEditorTreeViewInteractionResult& result, + const Widgets::UIEditorTreeViewMetrics& metrics) { + std::size_t targetVisibleIndex = result.selectedVisibleIndex; + if (targetVisibleIndex == UIEditorTreeViewInvalidIndex && + selectionModel.HasSelection()) { + targetVisibleIndex = + FindVisibleIndexForItemId(layout, items, selectionModel.GetSelectedId()); + } + + if (targetVisibleIndex == UIEditorTreeViewInvalidIndex) { + return; + } + + const float adjustedOffset = + ResolveVisibleOffset(layout, targetVisibleIndex, metrics); + if (adjustedOffset == state.verticalOffset) { + return; + } + + state.verticalOffset = adjustedOffset; + layout = BuildLayout(state, bounds, items, expansionModel, metrics); +} + } // namespace UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction( UIEditorTreeViewInteractionState& state, - ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, + UISelectionModel& selectionModel, ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, const ::XCEngine::UI::UIRect& bounds, const std::vector& items, const std::vector& inputEvents, const Widgets::UIEditorTreeViewMetrics& metrics) { Widgets::UIEditorTreeViewLayout layout = - BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics); + BuildLayout(state, bounds, items, expansionModel, metrics); SyncKeyboardNavigation(state, selectionModel, layout, items); SyncSelectionAnchor(state, selectionModel); SyncHoverTarget(state, layout, items); + SyncScrollState(state); UIEditorTreeViewInteractionResult interactionResult = {}; for (const UIInputEvent& event : inputEvents) { + state.scrollViewInteractionState.scrollViewState.focused = + state.treeViewState.focused; + const std::vector scrollEvents = { event }; + const UIEditorScrollViewInteractionFrame scrollFrame = + UpdateUIEditorScrollViewInteraction( + state.scrollViewInteractionState, + state.verticalOffset, + bounds, + MeasureUIEditorTreeViewContentHeight(items, expansionModel, metrics), + scrollEvents, + metrics.scrollViewMetrics); + state.treeViewState.focused = + state.scrollViewInteractionState.scrollViewState.focused; + SyncScrollState(state); + layout = BuildLayout(state, bounds, items, expansionModel, metrics); + if (ShouldUsePointerPosition(event)) { state.pointerPosition = event.position; state.hasPointerPosition = true; @@ -468,6 +568,7 @@ UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction( case UIInputEventType::PointerMove: case UIInputEventType::PointerEnter: + case UIInputEventType::PointerWheel: break; case UIInputEventType::PointerLeave: @@ -586,9 +687,7 @@ UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction( case UIInputEventType::KeyDown: if (state.treeViewState.focused && - !event.modifiers.control && - !event.modifiers.alt && - !event.modifiers.super) { + !HasNavigationModifiers(event.modifiers)) { if (event.keyCode == static_cast(KeyCode::F2) && selectionModel.HasSelection()) { eventResult.renameRequested = true; @@ -625,7 +724,12 @@ UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction( break; } - layout = BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics); + eventResult.consumed = + eventResult.consumed || + scrollFrame.result.consumed || + scrollFrame.result.offsetChanged; + + layout = BuildLayout(state, bounds, items, expansionModel, metrics); if (eventResult.expansionChanged && !eventResult.toggledItemId.empty() && !expansionModel.IsExpanded(eventResult.toggledItemId)) { @@ -637,9 +741,25 @@ UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction( eventResult.toggledItemId, eventResult); } + + if (eventResult.selectionChanged || + eventResult.keyboardNavigated || + eventResult.expansionChanged) { + EnsureVisibleSelection( + state, + selectionModel, + items, + expansionModel, + bounds, + layout, + eventResult, + metrics); + } + SyncKeyboardNavigation(state, selectionModel, layout, items); SyncSelectionAnchor(state, selectionModel); SyncHoverTarget(state, layout, items); + SyncScrollState(state); if (eventResult.hitTarget.kind == UIEditorTreeViewHitTargetKind::None && state.hasPointerPosition) { eventResult.hitTarget = HitTestUIEditorTreeView(layout, state.pointerPosition); @@ -659,10 +779,11 @@ UIEditorTreeViewInteractionFrame UpdateUIEditorTreeViewInteraction( } } - layout = BuildUIEditorTreeViewLayout(bounds, items, expansionModel, metrics); + layout = BuildLayout(state, bounds, items, expansionModel, metrics); SyncKeyboardNavigation(state, selectionModel, layout, items); SyncSelectionAnchor(state, selectionModel); SyncHoverTarget(state, layout, items); + SyncScrollState(state); if (interactionResult.hitTarget.kind == UIEditorTreeViewHitTargetKind::None && state.hasPointerPosition) { interactionResult.hitTarget = HitTestUIEditorTreeView(layout, state.pointerPosition); diff --git a/new_editor/src/Foundation/UIEditorTheme.cpp b/new_editor/src/Foundation/UIEditorTheme.cpp index b2855516..c6d7ee95 100644 --- a/new_editor/src/Foundation/UIEditorTheme.cpp +++ b/new_editor/src/Foundation/UIEditorTheme.cpp @@ -80,6 +80,13 @@ Widgets::UIEditorTreeViewPalette BuildTreeViewPalette() { palette.rowSelectedFocusedColor = kSurfaceActive; palette.disclosureColor = kTextMuted; palette.textColor = kTextPrimary; + palette.scrollViewPalette.surfaceColor = palette.surfaceColor; + palette.scrollViewPalette.borderColor = palette.borderColor; + palette.scrollViewPalette.focusedBorderColor = palette.focusedBorderColor; + palette.scrollViewPalette.scrollbarTrackColor = kSurfaceLower; + palette.scrollViewPalette.scrollbarThumbColor = kSurfaceHover; + palette.scrollViewPalette.scrollbarThumbHoverColor = kSurfaceActive; + palette.scrollViewPalette.scrollbarThumbActiveColor = UIColor(0.20f, 0.20f, 0.20f, 1.0f); return palette; } @@ -98,6 +105,12 @@ Widgets::UIEditorTreeViewMetrics BuildTreeViewMetrics() { metrics.cornerRounding = 0.0f; metrics.borderThickness = 0.0f; metrics.focusedBorderThickness = 0.0f; + metrics.scrollViewMetrics.scrollbarWidth = 8.0f; + metrics.scrollViewMetrics.scrollbarInset = 3.0f; + metrics.scrollViewMetrics.minThumbHeight = 28.0f; + metrics.scrollViewMetrics.cornerRounding = metrics.cornerRounding; + metrics.scrollViewMetrics.borderThickness = metrics.borderThickness; + metrics.scrollViewMetrics.focusedBorderThickness = metrics.focusedBorderThickness; return metrics; }