Add native scrolling to new editor tree views
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -1101,7 +1101,8 @@ std::vector<UIInputEvent> 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 =
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Collections/UIEditorScrollView.h>
|
||||
|
||||
#include <XCEngine/UI/DrawData.h>
|
||||
#include <XCEngine/UI/Widgets/UIExpansionModel.h>
|
||||
#include <XCEngine/UI/Widgets/UISelectionModel.h>
|
||||
@@ -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<std::size_t> 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<UIEditorTreeViewItem>& items,
|
||||
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
|
||||
const UIEditorTreeViewMetrics& 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 = 0.0f);
|
||||
|
||||
UIEditorTreeViewHitTarget HitTestUIEditorTreeView(
|
||||
const UIEditorTreeViewLayout& layout,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEditor/Collections/UIEditorScrollViewInteraction.h>
|
||||
#include <XCEditor/Collections/UIEditorTreeView.h>
|
||||
|
||||
#include <XCEngine/UI/Types.h>
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <XCEngine/Input/InputTypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& 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<Widgets::UIEditorTreeViewItem>& items,
|
||||
const std::vector<UIInputEvent>& 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<UIInputEvent> 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<std::int32_t>(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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user