refactor(new_editor/app): reorganize host structure and add smoke test
This commit is contained in:
229
new_editor/app/Features/Project/ProjectPanelInteraction.cpp
Normal file
229
new_editor/app/Features/Project/ProjectPanelInteraction.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user