Files
XCEngine/new_editor/app/Features/Project/ProjectPanelInteraction.cpp

230 lines
8.9 KiB
C++

#include "ProjectPanelSupport.h"
#include <windows.h>
namespace XCEngine::UI::Editor::App {
using namespace ProjectPanelSupport;
void ProjectPanel::ResetTransientFrames() {
m_treeFrame = {};
m_frameEvents.clear();
m_layout = {};
m_hoveredAssetItemId.clear();
m_hoveredBreadcrumbIndex = kInvalidLayoutIndex;
m_pressedBreadcrumbIndex = kInvalidLayoutIndex;
m_splitterHovered = false;
m_splitterDragging = false;
}
void ProjectPanel::Update(
const UIEditorPanelContentHostFrame& contentHostFrame,
const std::vector<UIInputEvent>& inputEvents,
bool allowInteraction,
bool panelActive) {
m_requestPointerCapture = false;
m_requestPointerRelease = false;
m_frameEvents.clear();
const UIEditorPanelContentHostPanelState* panelState =
FindMountedProjectPanel(contentHostFrame);
if (panelState == nullptr) {
if (m_splitterDragging) {
m_requestPointerRelease = true;
}
m_visible = false;
ResetTransientFrames();
return;
}
if (m_browserModel.GetTreeItems().empty()) {
m_browserModel.Refresh();
SyncCurrentFolderSelection();
}
m_visible = true;
const std::vector<UIInputEvent> filteredEvents =
FilterProjectPanelInputEvents(
panelState->bounds,
inputEvents,
allowInteraction,
panelActive,
m_splitterDragging);
m_navigationWidth = ClampNavigationWidth(m_navigationWidth, panelState->bounds.width);
m_layout = BuildLayout(panelState->bounds);
const Widgets::UIEditorTreeViewMetrics treeMetrics = BuildEditorTreeViewMetrics();
const std::vector<UIInputEvent> treeEvents =
FilterTreeInputEvents(filteredEvents, m_splitterDragging);
m_treeFrame = UpdateUIEditorTreeViewInteraction(
m_treeInteractionState,
m_folderSelection,
m_folderExpansion,
m_layout.treeRect,
m_browserModel.GetTreeItems(),
treeEvents,
treeMetrics);
if (m_treeFrame.result.selectionChanged &&
!m_treeFrame.result.selectedItemId.empty() &&
m_treeFrame.result.selectedItemId != m_browserModel.GetCurrentFolderId()) {
NavigateToFolder(m_treeFrame.result.selectedItemId, EventSource::Tree);
m_layout = BuildLayout(panelState->bounds);
}
for (const UIInputEvent& event : filteredEvents) {
switch (event.type) {
case UIInputEventType::FocusLost:
m_hoveredAssetItemId.clear();
m_hoveredBreadcrumbIndex = kInvalidLayoutIndex;
m_pressedBreadcrumbIndex = kInvalidLayoutIndex;
m_splitterHovered = false;
if (m_splitterDragging) {
m_splitterDragging = false;
m_requestPointerRelease = true;
}
break;
case UIInputEventType::PointerMove: {
if (m_splitterDragging) {
m_navigationWidth =
ClampNavigationWidth(event.position.x - panelState->bounds.x, panelState->bounds.width);
m_layout = BuildLayout(panelState->bounds);
}
m_splitterHovered =
m_splitterDragging || ContainsPoint(m_layout.dividerRect, event.position);
m_hoveredBreadcrumbIndex = HitTestBreadcrumbItem(event.position);
const std::size_t hoveredAssetIndex = HitTestAssetTile(event.position);
const auto& assetEntries = m_browserModel.GetAssetEntries();
m_hoveredAssetItemId =
hoveredAssetIndex < assetEntries.size()
? assetEntries[hoveredAssetIndex].itemId
: std::string();
break;
}
case UIInputEventType::PointerLeave:
if (!m_splitterDragging) {
m_splitterHovered = false;
}
m_hoveredBreadcrumbIndex = kInvalidLayoutIndex;
m_hoveredAssetItemId.clear();
break;
case UIInputEventType::PointerButtonDown:
if (event.pointerButton == ::XCEngine::UI::UIPointerButton::Left) {
if (ContainsPoint(m_layout.dividerRect, event.position)) {
m_splitterDragging = true;
m_splitterHovered = true;
m_pressedBreadcrumbIndex = kInvalidLayoutIndex;
m_requestPointerCapture = true;
break;
}
m_pressedBreadcrumbIndex = HitTestBreadcrumbItem(event.position);
if (!ContainsPoint(m_layout.gridRect, event.position)) {
break;
}
const auto& assetEntries = m_browserModel.GetAssetEntries();
const std::size_t hitIndex = HitTestAssetTile(event.position);
if (hitIndex >= assetEntries.size()) {
if (m_assetSelection.HasSelection()) {
m_assetSelection.ClearSelection();
EmitSelectionClearedEvent(EventSource::Background);
}
break;
}
const AssetEntry& assetEntry = assetEntries[hitIndex];
const bool alreadySelected = m_assetSelection.IsSelected(assetEntry.itemId);
const bool selectionChanged = m_assetSelection.SetSelection(assetEntry.itemId);
if (selectionChanged) {
EmitEvent(EventKind::AssetSelected, EventSource::GridPrimary, &assetEntry);
}
const std::uint64_t nowMs = GetTickCount64();
const std::uint64_t doubleClickThresholdMs =
static_cast<std::uint64_t>(GetDoubleClickTime());
const bool doubleClicked =
alreadySelected &&
m_lastPrimaryClickedAssetId == assetEntry.itemId &&
nowMs >= m_lastPrimaryClickTimeMs &&
nowMs - m_lastPrimaryClickTimeMs <= doubleClickThresholdMs;
m_lastPrimaryClickedAssetId = assetEntry.itemId;
m_lastPrimaryClickTimeMs = nowMs;
if (!doubleClicked) {
break;
}
if (assetEntry.directory) {
NavigateToFolder(assetEntry.itemId, EventSource::GridDoubleClick);
m_layout = BuildLayout(panelState->bounds);
m_hoveredAssetItemId.clear();
} else {
EmitEvent(EventKind::AssetOpened, EventSource::GridDoubleClick, &assetEntry);
}
break;
}
if (event.pointerButton == ::XCEngine::UI::UIPointerButton::Right &&
ContainsPoint(m_layout.gridRect, event.position)) {
const auto& assetEntries = m_browserModel.GetAssetEntries();
const std::size_t hitIndex = HitTestAssetTile(event.position);
if (hitIndex >= assetEntries.size()) {
EmitEvent(
EventKind::ContextMenuRequested,
EventSource::Background,
static_cast<const AssetEntry*>(nullptr));
break;
}
const AssetEntry& assetEntry = assetEntries[hitIndex];
if (!m_assetSelection.IsSelected(assetEntry.itemId)) {
m_assetSelection.SetSelection(assetEntry.itemId);
EmitEvent(EventKind::AssetSelected, EventSource::GridSecondary, &assetEntry);
}
EmitEvent(EventKind::ContextMenuRequested, EventSource::GridSecondary, &assetEntry);
}
break;
case UIInputEventType::PointerButtonUp:
if (event.pointerButton != ::XCEngine::UI::UIPointerButton::Left) {
break;
}
if (m_splitterDragging) {
m_splitterDragging = false;
m_splitterHovered = ContainsPoint(m_layout.dividerRect, event.position);
m_requestPointerRelease = true;
break;
}
{
const std::size_t releasedBreadcrumbIndex =
HitTestBreadcrumbItem(event.position);
if (m_pressedBreadcrumbIndex != kInvalidLayoutIndex &&
m_pressedBreadcrumbIndex == releasedBreadcrumbIndex &&
releasedBreadcrumbIndex < m_layout.breadcrumbItems.size()) {
const BreadcrumbItemLayout& item =
m_layout.breadcrumbItems[releasedBreadcrumbIndex];
if (item.clickable) {
NavigateToFolder(item.targetFolderId, EventSource::Breadcrumb);
m_layout = BuildLayout(panelState->bounds);
}
}
m_pressedBreadcrumbIndex = kInvalidLayoutIndex;
}
break;
default:
break;
}
}
}
} // namespace XCEngine::UI::Editor::App