#include "ProductHierarchyPanel.h" #include "Icons/ProductBuiltInIcons.h" #include "Panels/ProductTreeViewStyle.h" #include #include #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::UI::UIDrawList; using ::XCEngine::UI::UIInputEvent; using ::XCEngine::UI::UIInputEventType; using ::XCEngine::UI::UIPoint; using ::XCEngine::UI::UIRect; using Widgets::AppendUIEditorTreeViewBackground; using Widgets::AppendUIEditorTreeViewForeground; constexpr std::string_view kHierarchyPanelId = "hierarchy"; bool ContainsPoint(const UIRect& rect, const UIPoint& point) { return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height; } std::vector FilterHierarchyInputEvents( const UIRect& bounds, const std::vector& inputEvents, bool allowInteraction, bool panelActive) { if (!allowInteraction) { return {}; } std::vector filteredEvents = {}; filteredEvents.reserve(inputEvents.size()); for (const UIInputEvent& event : inputEvents) { switch (event.type) { case UIInputEventType::PointerMove: case UIInputEventType::PointerButtonDown: case UIInputEventType::PointerButtonUp: case UIInputEventType::PointerWheel: if (ContainsPoint(bounds, event.position)) { filteredEvents.push_back(event); } break; case UIInputEventType::PointerLeave: filteredEvents.push_back(event); break; case UIInputEventType::FocusGained: case UIInputEventType::FocusLost: case UIInputEventType::KeyDown: case UIInputEventType::KeyUp: case UIInputEventType::Character: if (panelActive) { filteredEvents.push_back(event); } break; default: break; } } return filteredEvents; } ::XCEngine::UI::UITextureHandle ResolveGameObjectIcon( const ProductBuiltInIcons* icons) { return icons != nullptr ? icons->Resolve(ProductBuiltInIconKind::GameObject) : ::XCEngine::UI::UITextureHandle {}; } } // namespace void ProductHierarchyPanel::Initialize() { RebuildItems(); } void ProductHierarchyPanel::SetBuiltInIcons(const ProductBuiltInIcons* icons) { m_icons = icons; RebuildItems(); } const UIEditorPanelContentHostPanelState* ProductHierarchyPanel::FindMountedHierarchyPanel( const UIEditorPanelContentHostFrame& contentHostFrame) const { for (const UIEditorPanelContentHostPanelState& panelState : contentHostFrame.panelStates) { if (panelState.panelId == kHierarchyPanelId && panelState.mounted) { return &panelState; } } return nullptr; } void ProductHierarchyPanel::RebuildItems() { const auto icon = ResolveGameObjectIcon(m_icons); const std::string previousSelection = m_selection.HasSelection() ? m_selection.GetSelectedId() : std::string(); m_treeItems = { { "main-camera", "Main Camera", 0u, true, 0.0f, icon }, { "directional-light", "Directional Light", 0u, true, 0.0f, icon }, { "player", "Player", 0u, false, 0.0f, icon }, { "player/camera-pivot", "Camera Pivot", 1u, true, 0.0f, icon }, { "player/mesh", "Mesh", 1u, true, 0.0f, icon }, { "environment", "Environment", 0u, false, 0.0f, icon }, { "environment/ground", "Ground", 1u, true, 0.0f, icon }, { "environment/props", "Props", 1u, false, 0.0f, icon }, { "environment/props/crate-01", "Crate_01", 2u, true, 0.0f, icon }, { "environment/props/barrel-01", "Barrel_01", 2u, true, 0.0f, icon } }; m_expansion.Expand("player"); m_expansion.Expand("environment"); m_expansion.Expand("environment/props"); if (!previousSelection.empty()) { for (const Widgets::UIEditorTreeViewItem& item : m_treeItems) { if (item.itemId == previousSelection) { m_selection.SetSelection(previousSelection); return; } } } if (!m_treeItems.empty()) { m_selection.SetSelection(m_treeItems.front().itemId); } } void ProductHierarchyPanel::Update( const UIEditorPanelContentHostFrame& contentHostFrame, const std::vector& inputEvents, bool allowInteraction, bool panelActive) { const UIEditorPanelContentHostPanelState* panelState = FindMountedHierarchyPanel(contentHostFrame); if (panelState == nullptr) { m_visible = false; m_treeFrame = {}; return; } if (m_treeItems.empty()) { RebuildItems(); } m_visible = true; m_treeFrame = UpdateUIEditorTreeViewInteraction( m_treeInteractionState, m_selection, m_expansion, panelState->bounds, m_treeItems, FilterHierarchyInputEvents(panelState->bounds, inputEvents, allowInteraction, panelActive), BuildProductTreeViewMetrics()); } void ProductHierarchyPanel::Append(UIDrawList& drawList) const { if (!m_visible || m_treeFrame.layout.bounds.width <= 0.0f || m_treeFrame.layout.bounds.height <= 0.0f) { return; } const Widgets::UIEditorTreeViewPalette palette = BuildProductTreeViewPalette(); const Widgets::UIEditorTreeViewMetrics metrics = BuildProductTreeViewMetrics(); AppendUIEditorTreeViewBackground( drawList, m_treeFrame.layout, m_treeItems, m_selection, m_treeInteractionState.treeViewState, palette, metrics); AppendUIEditorTreeViewForeground( drawList, m_treeFrame.layout, m_treeItems, palette, metrics); } } // namespace XCEngine::UI::Editor::App