Refine editor viewport and interaction workflow
This commit is contained in:
@@ -16,6 +16,13 @@ namespace Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename Fn>
|
||||
void QueueDeferredAction(std::function<void()>& pendingAction, Fn&& fn) {
|
||||
if (!pendingAction) {
|
||||
pendingAction = std::forward<Fn>(fn);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawProjectFolderTreePrefix(const UI::TreeNodePrefixContext& context) {
|
||||
if (!context.drawList) {
|
||||
return;
|
||||
@@ -186,6 +193,7 @@ void ProjectPanel::Render() {
|
||||
|
||||
auto& manager = m_context->GetProjectManager();
|
||||
BeginAssetDragDropFrame();
|
||||
m_deferredContextAction = {};
|
||||
RenderToolbar();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ProjectBrowserSurfaceColor());
|
||||
@@ -215,6 +223,12 @@ void ProjectPanel::Render() {
|
||||
|
||||
FinalizeAssetDragDrop(manager);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (m_deferredContextAction) {
|
||||
auto deferredAction = std::move(m_deferredContextAction);
|
||||
m_deferredContextAction = {};
|
||||
deferredAction();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectPanel::RenderToolbar() {
|
||||
@@ -246,6 +260,7 @@ void ProjectPanel::RenderToolbar() {
|
||||
}
|
||||
|
||||
void ProjectPanel::RenderFolderTreePane(IProjectManager& manager) {
|
||||
auto* managerPtr = &manager;
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ProjectNavigationPaneBackgroundColor());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, UI::ProjectNavigationPanePadding());
|
||||
const bool open = ImGui::BeginChild("ProjectFolderTree", ImVec2(m_navigationWidth, 0.0f), false);
|
||||
@@ -266,6 +281,17 @@ void ProjectPanel::RenderFolderTreePane(IProjectManager& manager) {
|
||||
UI::DrawEmptyState("No Assets Folder");
|
||||
}
|
||||
|
||||
if (UI::BeginContextMenuForWindow("##ProjectFolderTreeContext")) {
|
||||
Actions::DrawMenuAction(Actions::MakeCreateFolderAction(), [&]() {
|
||||
QueueDeferredAction(m_deferredContextAction, [this, managerPtr]() {
|
||||
if (AssetItemPtr createdFolder = Commands::CreateFolder(*managerPtr, "New Folder")) {
|
||||
BeginRename(createdFolder);
|
||||
}
|
||||
});
|
||||
});
|
||||
UI::EndContextMenu();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
@@ -298,9 +324,6 @@ void ProjectPanel::RenderFolderTreeNode(
|
||||
manager.NavigateToFolder(folder);
|
||||
}
|
||||
|
||||
if (node.secondaryClicked) {
|
||||
Actions::HandleProjectItemContextRequest(manager, folder, m_itemContextMenu);
|
||||
}
|
||||
};
|
||||
|
||||
const UI::TreeNodeResult node = UI::DrawTreeNode(
|
||||
@@ -323,6 +346,7 @@ void ProjectPanel::RenderFolderTreeNode(
|
||||
}
|
||||
|
||||
void ProjectPanel::RenderBrowserPane(IProjectManager& manager) {
|
||||
auto* managerPtr = &manager;
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ProjectBrowserPaneBackgroundColor());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
const bool open = ImGui::BeginChild("ProjectBrowser", ImVec2(0.0f, 0.0f), false);
|
||||
@@ -361,7 +385,9 @@ void ProjectPanel::RenderBrowserPane(IProjectManager& manager) {
|
||||
}
|
||||
|
||||
const float tileWidth = UI::AssetTileSize().x;
|
||||
const float tileHeight = UI::AssetTileSize().y;
|
||||
const float spacing = UI::AssetGridSpacing().x;
|
||||
const float rowSpacing = UI::AssetGridSpacing().y;
|
||||
const float panelWidth = ImGui::GetContentRegionAvail().x;
|
||||
int columns = static_cast<int>((panelWidth + spacing) / (tileWidth + spacing));
|
||||
if (columns < 1) {
|
||||
@@ -369,26 +395,33 @@ void ProjectPanel::RenderBrowserPane(IProjectManager& manager) {
|
||||
}
|
||||
|
||||
AssetItemPtr pendingSelection;
|
||||
AssetItemPtr pendingContextTarget;
|
||||
AssetItemPtr pendingOpenTarget;
|
||||
const std::string selectedItemPath = manager.GetSelectedItemPath();
|
||||
const ImVec2 gridOrigin = ImGui::GetCursorPos();
|
||||
for (int visibleIndex = 0; visibleIndex < static_cast<int>(visibleItems.size()); ++visibleIndex) {
|
||||
if (visibleIndex > 0 && visibleIndex % columns != 0) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
const int column = visibleIndex % columns;
|
||||
const int row = visibleIndex / columns;
|
||||
ImGui::SetCursorPos(ImVec2(
|
||||
gridOrigin.x + column * (tileWidth + spacing),
|
||||
gridOrigin.y + row * (tileHeight + rowSpacing)));
|
||||
|
||||
const AssetItemPtr& item = visibleItems[visibleIndex];
|
||||
const AssetItemInteraction interaction = RenderAssetItem(item, selectedItemPath == item->fullPath);
|
||||
if (interaction.clicked) {
|
||||
pendingSelection = item;
|
||||
}
|
||||
if (interaction.contextRequested) {
|
||||
pendingContextTarget = item;
|
||||
}
|
||||
if (interaction.openRequested) {
|
||||
pendingOpenTarget = item;
|
||||
break;
|
||||
}
|
||||
if (m_deferredContextAction) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!visibleItems.empty()) {
|
||||
const int rowCount = (static_cast<int>(visibleItems.size()) + columns - 1) / columns;
|
||||
ImGui::SetCursorPosY(gridOrigin.y + rowCount * tileHeight + (rowCount - 1) * rowSpacing);
|
||||
}
|
||||
|
||||
if (visibleItems.empty() && !search.empty()) {
|
||||
@@ -397,29 +430,41 @@ void ProjectPanel::RenderBrowserPane(IProjectManager& manager) {
|
||||
"No assets match the current search");
|
||||
}
|
||||
|
||||
Actions::HandleProjectBackgroundPrimaryClick(manager, m_renameState);
|
||||
if (pendingSelection) {
|
||||
manager.SetSelectedItem(pendingSelection);
|
||||
}
|
||||
if (pendingContextTarget) {
|
||||
Actions::HandleProjectItemContextRequest(manager, pendingContextTarget, m_itemContextMenu);
|
||||
}
|
||||
if (pendingOpenTarget) {
|
||||
Actions::OpenProjectAsset(*m_context, pendingOpenTarget);
|
||||
}
|
||||
Actions::DrawProjectItemContextPopup(*m_context, m_itemContextMenu);
|
||||
Actions::RequestProjectEmptyContextPopup(m_emptyContextMenu);
|
||||
Actions::DrawProjectEmptyContextPopup(m_emptyContextMenu, [&]() {
|
||||
if (AssetItemPtr createdFolder = Commands::CreateFolder(manager, "New Folder")) {
|
||||
BeginRename(createdFolder);
|
||||
if (!m_deferredContextAction) {
|
||||
Actions::HandleProjectBackgroundPrimaryClick(manager, m_renameState);
|
||||
if (pendingSelection) {
|
||||
manager.SetSelectedItem(pendingSelection);
|
||||
}
|
||||
});
|
||||
if (pendingOpenTarget) {
|
||||
Actions::OpenProjectAsset(*m_context, pendingOpenTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if (UI::BeginContextMenuForWindow("##ProjectBrowserContext")) {
|
||||
Actions::DrawMenuAction(Actions::MakeCreateFolderAction(), [&]() {
|
||||
QueueDeferredAction(m_deferredContextAction, [this, managerPtr]() {
|
||||
if (AssetItemPtr createdFolder = Commands::CreateFolder(*managerPtr, "New Folder")) {
|
||||
BeginRename(createdFolder);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (manager.CanNavigateBack()) {
|
||||
Actions::DrawMenuSeparator();
|
||||
Actions::DrawMenuAction(Actions::MakeNavigateBackAction(true), [&]() {
|
||||
QueueDeferredAction(m_deferredContextAction, [managerPtr]() {
|
||||
managerPtr->NavigateBack();
|
||||
});
|
||||
});
|
||||
}
|
||||
UI::EndContextMenu();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void ProjectPanel::RenderBrowserHeader(IProjectManager& manager) {
|
||||
auto* managerPtr = &manager;
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ProjectBrowserHeaderBackgroundColor());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 5.0f));
|
||||
const bool open = ImGui::BeginChild(
|
||||
@@ -445,7 +490,11 @@ void ProjectPanel::RenderBrowserHeader(IProjectManager& manager) {
|
||||
"Assets",
|
||||
manager.GetPathDepth(),
|
||||
[&](size_t index) { return manager.GetPathName(index); },
|
||||
[&](size_t index) { manager.NavigateToIndex(index); });
|
||||
[&](size_t index) {
|
||||
QueueDeferredAction(m_deferredContextAction, [managerPtr, index]() {
|
||||
managerPtr->NavigateToIndex(index);
|
||||
});
|
||||
});
|
||||
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const ImVec2 windowMin = ImGui::GetWindowPos();
|
||||
@@ -478,41 +527,54 @@ ProjectPanel::AssetItemInteraction ProjectPanel::RenderAssetItem(const AssetItem
|
||||
UI::DrawAssetIcon(drawList, iconMin, iconMax, iconKind);
|
||||
},
|
||||
tileOptions);
|
||||
const bool secondaryClicked = !isRenaming && ImGui::IsItemClicked(ImGuiMouseButton_Right);
|
||||
|
||||
if (isRenaming) {
|
||||
const ImVec2 restoreCursor = ImGui::GetCursorPos();
|
||||
ImGui::SetCursorScreenPos(tile.labelMin);
|
||||
ImGui::SetNextItemWidth(tile.labelMax.x - tile.labelMin.x);
|
||||
if (m_renameState.ConsumeFocusRequest()) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
const bool submitted = ImGui::InputText(
|
||||
const float renameWidth = tile.labelMax.x - tile.labelMin.x;
|
||||
const float renameOffsetY = (std::max)(0.0f, (tile.labelMax.y - tile.labelMin.y - UI::InlineRenameFieldHeight()) * 0.5f);
|
||||
const UI::InlineRenameFieldResult renameField = UI::DrawInlineRenameFieldAt(
|
||||
"##Rename",
|
||||
ImVec2(tile.labelMin.x, tile.labelMin.y + renameOffsetY),
|
||||
m_renameState.Buffer(),
|
||||
m_renameState.BufferSize(),
|
||||
ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll);
|
||||
const bool cancelRequested = ImGui::IsItemActive() && ImGui::IsKeyPressed(ImGuiKey_Escape);
|
||||
const bool deactivated = ImGui::IsItemDeactivated();
|
||||
ImGui::SetCursorPos(restoreCursor);
|
||||
renameWidth,
|
||||
m_renameState.ConsumeFocusRequest());
|
||||
|
||||
if (cancelRequested) {
|
||||
if (renameField.cancelRequested) {
|
||||
CancelRename();
|
||||
} else if (submitted || deactivated) {
|
||||
} else if (renameField.submitted || renameField.deactivated) {
|
||||
CommitRename(m_context->GetProjectManager());
|
||||
}
|
||||
} else {
|
||||
if (tile.clicked) {
|
||||
interaction.clicked = true;
|
||||
}
|
||||
|
||||
if (tile.contextRequested) {
|
||||
interaction.contextRequested = true;
|
||||
if (secondaryClicked && item) {
|
||||
m_context->GetProjectManager().SetSelectedItem(item);
|
||||
}
|
||||
|
||||
RegisterFolderDropTarget(m_context->GetProjectManager(), item);
|
||||
Actions::BeginProjectAssetDrag(item, iconKind);
|
||||
|
||||
if (UI::BeginContextMenuForLastItem("##ProjectItemContext")) {
|
||||
Actions::DrawMenuAction(Actions::MakeOpenAssetAction(Commands::CanOpenAsset(item)), [&]() {
|
||||
QueueDeferredAction(m_deferredContextAction, [this, item]() {
|
||||
Actions::OpenProjectAsset(*m_context, item);
|
||||
});
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeAction("Rename", nullptr, false, item != nullptr), [&]() {
|
||||
QueueDeferredAction(m_deferredContextAction, [this, item]() {
|
||||
BeginRename(item);
|
||||
});
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeDeleteAssetAction(item != nullptr), [&]() {
|
||||
QueueDeferredAction(m_deferredContextAction, [this, item]() {
|
||||
Commands::DeleteAsset(m_context->GetProjectManager(), item);
|
||||
});
|
||||
});
|
||||
UI::EndContextMenu();
|
||||
}
|
||||
|
||||
if (tile.openRequested) {
|
||||
interaction.openRequested = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user