Route editor actions by active target

This commit is contained in:
2026-03-26 22:10:43 +08:00
parent 5c8042775c
commit 5735e769b0
21 changed files with 609 additions and 104 deletions

View File

@@ -17,6 +17,16 @@ struct ShortcutChord {
bool alt = false;
};
enum class ShortcutRoute {
Global,
FocusedWindow
};
struct ShortcutContext {
ShortcutRoute route = ShortcutRoute::Global;
bool enabled = true;
};
struct ActionBinding {
std::string label;
std::string shortcutLabel;
@@ -31,6 +41,14 @@ inline ShortcutChord Shortcut(ImGuiKey key, bool ctrl = false, bool shift = fals
return ShortcutChord{ key, ctrl, shift, alt };
}
inline ShortcutContext GlobalShortcutContext() {
return ShortcutContext{ ShortcutRoute::Global, true };
}
inline ShortcutContext FocusedWindowShortcutContext() {
return ShortcutContext{ ShortcutRoute::FocusedWindow, true };
}
inline ActionBinding MakeAction(
std::string label,
const char* shortcutLabel = nullptr,
@@ -118,11 +136,30 @@ inline bool IsShortcutPressed(const ShortcutChord& shortcut, const ImGuiIO& io)
return ImGui::IsKeyPressed(shortcut.key, false);
}
inline bool IsShortcutPressed(const ActionBinding& action) {
inline bool IsShortcutContextActive(const ShortcutContext& context) {
if (!context.enabled) {
return false;
}
switch (context.route) {
case ShortcutRoute::Global:
return true;
case ShortcutRoute::FocusedWindow:
return ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
}
return false;
}
inline bool IsShortcutPressed(const ActionBinding& action, const ShortcutContext& context) {
if (!action.enabled) {
return false;
}
if (!IsShortcutContextActive(context)) {
return false;
}
const ImGuiIO& io = ImGui::GetIO();
if (!action.allowWhenTextInput && io.WantTextInput) {
return false;
@@ -137,9 +174,13 @@ inline bool IsShortcutPressed(const ActionBinding& action) {
return false;
}
inline bool IsShortcutPressed(const ActionBinding& action) {
return IsShortcutPressed(action, GlobalShortcutContext());
}
template <typename ExecuteFn>
inline bool HandleShortcut(const ActionBinding& action, ExecuteFn&& execute) {
if (!IsShortcutPressed(action)) {
inline bool HandleShortcut(const ActionBinding& action, const ShortcutContext& context, ExecuteFn&& execute) {
if (!IsShortcutPressed(action, context)) {
return false;
}
@@ -147,6 +188,11 @@ inline bool HandleShortcut(const ActionBinding& action, ExecuteFn&& execute) {
return true;
}
template <typename ExecuteFn>
inline bool HandleShortcut(const ActionBinding& action, ExecuteFn&& execute) {
return HandleShortcut(action, GlobalShortcutContext(), std::forward<ExecuteFn>(execute));
}
} // namespace Actions
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Core/EditorActionRoute.h"
#include "Core/IEditorContext.h"
#include <imgui.h>
namespace XCEngine {
namespace Editor {
namespace Actions {
inline void ObserveFocusedActionRoute(IEditorContext& context, EditorActionRoute route) {
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
context.SetActiveActionRoute(route);
}
}
inline bool IsActionRouteActive(const IEditorContext& context, EditorActionRoute route) {
return context.GetActiveActionRoute() == route;
}
} // namespace Actions
} // namespace Editor
} // namespace XCEngine

View File

@@ -2,9 +2,11 @@
#include "ActionBinding.h"
#include "Core/IEditorContext.h"
#include "Core/IProjectManager.h"
#include "Core/ISelectionManager.h"
#include "Core/ISceneManager.h"
#include "Core/IUndoManager.h"
#include "Core/AssetItem.h"
#include <XCEngine/Components/GameObject.h>
@@ -128,15 +130,15 @@ inline ActionBinding MakeExitAction() {
}
inline ActionBinding MakeNavigateBackAction(bool enabled) {
return MakeAction("<", nullptr, false, enabled);
return MakeAction("<", "Alt+Left", false, enabled, false, Shortcut(ImGuiKey_LeftArrow, false, false, true));
}
inline ActionBinding MakeOpenAssetAction(bool enabled) {
return MakeAction("Open", nullptr, false, enabled);
return MakeAction("Open", "Enter", false, enabled, false, Shortcut(ImGuiKey_Enter), Shortcut(ImGuiKey_KeypadEnter));
}
inline ActionBinding MakeDeleteAssetAction(bool enabled = true) {
return MakeAction("Delete", nullptr, false, enabled);
return MakeAction("Delete", "Delete", false, enabled, false, Shortcut(ImGuiKey_Delete));
}
inline ActionBinding MakeCreateFolderAction(bool enabled = true) {
@@ -179,6 +181,17 @@ inline ::XCEngine::Components::GameObject* GetSelectedGameObject(IEditorContext&
return context.GetSceneManager().GetEntity(context.GetSelectionManager().GetSelectedEntity());
}
inline AssetItemPtr GetSelectedAssetItem(IEditorContext& context) {
auto& projectManager = context.GetProjectManager();
const int selectedIndex = projectManager.GetSelectedIndex();
auto& items = projectManager.GetCurrentItems();
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(items.size())) {
return nullptr;
}
return items[selectedIndex];
}
} // namespace Actions
} // namespace Editor
} // namespace XCEngine

View File

@@ -7,16 +7,12 @@
#include <XCEngine/Debug/Logger.h>
#include <XCEngine/Debug/FileLogSink.h>
#include <XCEngine/Debug/ConsoleLogSink.h>
#include <imgui_impl_win32.h>
#include <imgui_impl_dx12.h>
#include <imgui_internal.h>
#include <filesystem>
#include <stdio.h>
#include <windows.h>
#include <string>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
namespace {
std::string WideToUtf8(const std::wstring& value) {
@@ -66,10 +62,6 @@ std::string GetExecutableLogPath(const char* fileName) {
return GetExecutableDirectoryUtf8() + "\\" + fileName;
}
std::string BuildEditorConfigDirectory(const std::string& projectPath) {
return (std::filesystem::path(projectPath) / ".xceditor").string();
}
} // namespace
static LONG WINAPI GlobalExceptionFilter(EXCEPTION_POINTERS* exceptionPointers) {
@@ -142,12 +134,9 @@ bool Application::Initialize(HWND hwnd) {
PostMessageW(m_hwnd, WM_CLOSE, 0, 0);
}
});
ConfigureImGui(m_editorContext->GetProjectPath());
m_imguiSession.Initialize(m_editorContext->GetProjectPath());
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplDX12_Init(m_device, 3, DXGI_FORMAT_R8G8B8A8_UNORM, m_srvHeap,
m_srvHeap->GetCPUDescriptorHandleForHeapStart(),
m_srvHeap->GetGPUDescriptorHandleForHeapStart());
m_imguiBackend.Initialize(hwnd, m_device, m_srvHeap);
m_editorLayer = new EditorLayer();
m_editorLayer->SetContext(m_editorContext);
@@ -159,16 +148,14 @@ bool Application::Initialize(HWND hwnd) {
void Application::Shutdown() {
m_layerStack.onDetach();
SaveImGuiSettings();
if (m_editorContext && m_exitRequestedHandlerId) {
m_editorContext->GetEventBus().Unsubscribe<EditorExitRequestedEvent>(m_exitRequestedHandlerId);
m_exitRequestedHandlerId = 0;
}
ImGui_ImplDX12_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
m_imguiBackend.Shutdown();
m_imguiSession.Shutdown();
CleanupRenderTarget();
@@ -183,9 +170,7 @@ void Application::Shutdown() {
}
void Application::Render() {
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
m_imguiBackend.BeginFrame();
m_layerStack.onImGuiRender();
UpdateWindowTitle();
@@ -214,7 +199,7 @@ void Application::Render() {
ID3D12DescriptorHeap* heaps[] = { m_srvHeap };
m_commandList->SetDescriptorHeaps(1, heaps);
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), m_commandList);
m_imguiBackend.RenderDrawData(m_commandList);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
@@ -233,45 +218,6 @@ void Application::Render() {
}
}
void Application::ConfigureImGui(const std::string& projectPath) {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
ConfigureImGuiIniFile(projectPath, io);
if (ImFont* uiFont = io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/msyh.ttc", 15.0f)) {
io.FontDefault = uiFont;
} else {
io.FontDefault = io.Fonts->AddFontDefault();
}
unsigned char* pixels = nullptr;
int width = 0;
int height = 0;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
ApplyUnityDarkTheme();
}
void Application::ConfigureImGuiIniFile(const std::string& projectPath, ImGuiIO& io) {
const std::filesystem::path configDir = BuildEditorConfigDirectory(projectPath);
std::error_code ec;
std::filesystem::create_directories(configDir, ec);
m_imguiIniPath = (configDir / "imgui_layout.ini").string();
io.IniFilename = m_imguiIniPath.c_str();
}
void Application::SaveImGuiSettings() {
if (m_imguiIniPath.empty() || ImGui::GetCurrentContext() == nullptr) {
return;
}
ImGui::SaveIniSettingsToDisk(m_imguiIniPath.c_str());
}
void Application::UpdateWindowTitle() {
if (!m_hwnd || !m_editorContext) {
return;

View File

@@ -1,5 +1,8 @@
#pragma once
#include "UI/ImGuiBackendBridge.h"
#include "UI/ImGuiSession.h"
#include <memory>
#include <string>
#include <imgui.h>
@@ -8,8 +11,6 @@
#include <XCEngine/Core/LayerStack.h>
#include "Theme.h"
namespace XCEngine {
namespace Editor {
@@ -32,9 +33,6 @@ private:
Application() = default;
~Application() = default;
void ConfigureImGui(const std::string& projectPath);
void ConfigureImGuiIniFile(const std::string& projectPath, ImGuiIO& io);
void SaveImGuiSettings();
bool CreateDevice();
bool CreateRenderTarget();
void CleanupRenderTarget();
@@ -60,8 +58,9 @@ private:
Core::LayerStack m_layerStack;
EditorLayer* m_editorLayer = nullptr;
std::shared_ptr<IEditorContext> m_editorContext;
UI::ImGuiBackendBridge m_imguiBackend;
UI::ImGuiSession m_imguiSession;
uint64_t m_exitRequestedHandlerId = 0;
std::string m_imguiIniPath;
std::wstring m_lastWindowTitle;
};

View File

@@ -0,0 +1,13 @@
#pragma once
namespace XCEngine {
namespace Editor {
enum class EditorActionRoute {
None,
Hierarchy,
Project
};
} // namespace Editor
} // namespace XCEngine

View File

@@ -55,6 +55,14 @@ public:
IUndoManager& GetUndoManager() override {
return *m_undoManager;
}
void SetActiveActionRoute(EditorActionRoute route) override {
m_activeActionRoute = route;
}
EditorActionRoute GetActiveActionRoute() const override {
return m_activeActionRoute;
}
void SetProjectPath(const std::string& path) override {
m_projectPath = path;
@@ -70,6 +78,7 @@ private:
std::unique_ptr<SceneManager> m_sceneManager;
std::unique_ptr<UndoManager> m_undoManager;
std::unique_ptr<ProjectManager> m_projectManager;
EditorActionRoute m_activeActionRoute = EditorActionRoute::None;
std::string m_projectPath;
uint64_t m_entityDeletedHandlerId;
};

View File

@@ -25,6 +25,10 @@ struct EntityChangedEvent {
GameObjectID entityId;
};
struct EntityRenameRequestedEvent {
GameObjectID entityId;
};
struct EntityParentChangedEvent {
GameObjectID entityId;
GameObjectID oldParentId;

View File

@@ -1,5 +1,7 @@
#pragma once
#include "EditorActionRoute.h"
#include <memory>
#include <string>
@@ -21,6 +23,8 @@ public:
virtual ISceneManager& GetSceneManager() = 0;
virtual IProjectManager& GetProjectManager() = 0;
virtual IUndoManager& GetUndoManager() = 0;
virtual void SetActiveActionRoute(EditorActionRoute route) = 0;
virtual EditorActionRoute GetActiveActionRoute() const = 0;
virtual void SetProjectPath(const std::string& path) = 0;
virtual const std::string& GetProjectPath() const = 0;

View File

@@ -0,0 +1,65 @@
#pragma once
#include <d3d12.h>
#include <dxgi1_6.h>
#include <imgui.h>
#include <imgui_impl_dx12.h>
#include <imgui_impl_win32.h>
#include <windows.h>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
namespace XCEngine {
namespace Editor {
namespace UI {
class ImGuiBackendBridge {
public:
void Initialize(
HWND hwnd,
ID3D12Device* device,
ID3D12DescriptorHeap* srvHeap,
int frameCount = 3,
DXGI_FORMAT backBufferFormat = DXGI_FORMAT_R8G8B8A8_UNORM) {
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplDX12_Init(
device,
frameCount,
backBufferFormat,
srvHeap,
srvHeap->GetCPUDescriptorHandleForHeapStart(),
srvHeap->GetGPUDescriptorHandleForHeapStart());
m_initialized = true;
}
void Shutdown() {
if (!m_initialized) {
return;
}
ImGui_ImplDX12_Shutdown();
ImGui_ImplWin32_Shutdown();
m_initialized = false;
}
void BeginFrame() const {
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
}
void RenderDrawData(ID3D12GraphicsCommandList* commandList) const {
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), commandList);
}
static bool HandleWindowMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wParam, lParam) != 0;
}
private:
bool m_initialized = false;
};
} // namespace UI
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,77 @@
#pragma once
#include "BaseTheme.h"
#include <imgui.h>
#include <filesystem>
#include <string>
namespace XCEngine {
namespace Editor {
namespace UI {
class ImGuiSession {
public:
void Initialize(const std::string& projectPath) {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
ConfigureIniFile(projectPath, io);
ConfigureFonts(io);
ApplyBaseTheme(ImGui::GetStyle());
}
void Shutdown() {
SaveSettings();
if (ImGui::GetCurrentContext() != nullptr) {
ImGui::DestroyContext();
}
m_iniPath.clear();
}
void SaveSettings() const {
if (m_iniPath.empty() || ImGui::GetCurrentContext() == nullptr) {
return;
}
ImGui::SaveIniSettingsToDisk(m_iniPath.c_str());
}
const std::string& GetIniPath() const {
return m_iniPath;
}
private:
void ConfigureIniFile(const std::string& projectPath, ImGuiIO& io) {
const std::filesystem::path configDir = std::filesystem::path(projectPath) / ".xceditor";
std::error_code ec;
std::filesystem::create_directories(configDir, ec);
m_iniPath = (configDir / "imgui_layout.ini").string();
io.IniFilename = m_iniPath.c_str();
}
void ConfigureFonts(ImGuiIO& io) const {
if (ImFont* uiFont = io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/msyh.ttc", 15.0f)) {
io.FontDefault = uiFont;
} else {
io.FontDefault = io.Fonts->AddFontDefault();
}
unsigned char* pixels = nullptr;
int width = 0;
int height = 0;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
}
std::string m_iniPath;
};
} // namespace UI
} // namespace Editor
} // namespace XCEngine

View File

@@ -1,4 +1,5 @@
#include "Application.h"
#include "UI/ImGuiBackendBridge.h"
#include <imgui.h>
#include <windows.h>
#include <stdio.h>
@@ -72,10 +73,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow) {
return 0;
}
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
if (XCEngine::Editor::UI::ImGuiBackendBridge::HandleWindowMessage(hWnd, msg, wParam, lParam))
return true;
switch (msg) {

View File

@@ -1,4 +1,5 @@
#include "Actions/EditorActions.h"
#include "Actions/ActionRouting.h"
#include "Commands/EntityCommands.h"
#include "HierarchyPanel.h"
#include "Core/IEditorContext.h"
@@ -20,6 +21,7 @@ HierarchyPanel::HierarchyPanel() : Panel("Hierarchy") {
HierarchyPanel::~HierarchyPanel() {
if (m_context) {
m_context->GetEventBus().Unsubscribe<SelectionChangedEvent>(m_selectionHandlerId);
m_context->GetEventBus().Unsubscribe<EntityRenameRequestedEvent>(m_renameRequestHandlerId);
}
}
@@ -29,6 +31,11 @@ void HierarchyPanel::OnAttach() {
OnSelectionChanged(event);
}
);
m_renameRequestHandlerId = m_context->GetEventBus().Subscribe<EntityRenameRequestedEvent>(
[this](const EntityRenameRequestedEvent& event) {
OnRenameRequested(event);
}
);
}
void HierarchyPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
@@ -37,12 +44,24 @@ void HierarchyPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
}
}
void HierarchyPanel::OnRenameRequested(const EntityRenameRequestedEvent& event) {
if (!m_context || event.entityId == 0) {
return;
}
if (auto* gameObject = m_context->GetSceneManager().GetEntity(event.entityId)) {
BeginRename(gameObject);
}
}
void HierarchyPanel::Render() {
UI::PanelWindowScope panel(m_name.c_str());
if (!panel.IsOpen()) {
return;
}
Actions::ObserveFocusedActionRoute(*m_context, EditorActionRoute::Hierarchy);
RenderSearchBar();
HandleKeyboardShortcuts();
@@ -299,26 +318,24 @@ void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObje
void HierarchyPanel::HandleKeyboardShortcuts() {
auto& sceneManager = m_context->GetSceneManager();
auto& selectionManager = m_context->GetSelectionManager();
::XCEngine::Components::GameObject* selectedGameObject = sceneManager.GetEntity(selectionManager.GetSelectedEntity());
if (ImGui::IsWindowFocused()) {
Actions::HandleShortcut(Actions::MakeDeleteEntityAction(selectedGameObject), [&]() {
Commands::DeleteEntity(*m_context, selectedGameObject->GetID());
});
Actions::HandleShortcut(Actions::MakeRenameEntityAction(selectedGameObject), [&]() {
BeginRename(selectedGameObject);
});
Actions::HandleShortcut(Actions::MakeCopyEntityAction(selectedGameObject), [&]() {
Commands::CopyEntity(*m_context, selectedGameObject->GetID());
});
Actions::HandleShortcut(Actions::MakePasteEntityAction(*m_context), [&]() {
Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0);
});
Actions::HandleShortcut(Actions::MakeDuplicateEntityAction(selectedGameObject), [&]() {
Commands::DuplicateEntity(*m_context, selectedGameObject->GetID());
});
}
const Actions::ShortcutContext shortcutContext = Actions::FocusedWindowShortcutContext();
Actions::HandleShortcut(Actions::MakeDeleteEntityAction(selectedGameObject), shortcutContext, [&]() {
Commands::DeleteEntity(*m_context, selectedGameObject->GetID());
});
Actions::HandleShortcut(Actions::MakeRenameEntityAction(selectedGameObject), shortcutContext, [&]() {
BeginRename(selectedGameObject);
});
Actions::HandleShortcut(Actions::MakeCopyEntityAction(selectedGameObject), shortcutContext, [&]() {
Commands::CopyEntity(*m_context, selectedGameObject->GetID());
});
Actions::HandleShortcut(Actions::MakePasteEntityAction(*m_context), shortcutContext, [&]() {
Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0);
});
Actions::HandleShortcut(Actions::MakeDuplicateEntityAction(selectedGameObject), shortcutContext, [&]() {
Commands::DuplicateEntity(*m_context, selectedGameObject->GetID());
});
}
bool HierarchyPanel::PassesFilter(::XCEngine::Components::GameObject* gameObject, const std::string& filter) {

View File

@@ -20,6 +20,7 @@ public:
private:
void OnSelectionChanged(const struct SelectionChangedEvent& event);
void OnRenameRequested(const struct EntityRenameRequestedEvent& event);
void RenderSearchBar();
void RenderEntity(::XCEngine::Components::GameObject* gameObject, const std::string& filter);
void RenderContextMenu(::XCEngine::Components::GameObject* gameObject);
@@ -36,6 +37,7 @@ private:
UI::InlineTextEditState<uint64_t, 256> m_renameState;
SortMode m_sortMode = SortMode::Name;
uint64_t m_selectionHandlerId = 0;
uint64_t m_renameRequestHandlerId = 0;
};
}

View File

@@ -1,10 +1,13 @@
#include "Actions/ActionRouting.h"
#include "Actions/EditorActions.h"
#include "Commands/EntityCommands.h"
#include "Commands/ProjectCommands.h"
#include "Commands/SceneCommands.h"
#include "MenuBar.h"
#include "Core/EditorEvents.h"
#include "Core/EventBus.h"
#include "Core/IEditorContext.h"
#include "Core/IProjectManager.h"
#include "Core/ISceneManager.h"
#include "Core/IUndoManager.h"
#include "Core/ISelectionManager.h"
@@ -37,12 +40,14 @@ void MenuBar::HandleShortcuts() {
return;
}
Actions::HandleShortcut(Actions::MakeNewSceneAction(), [&]() { Commands::NewScene(*m_context); });
Actions::HandleShortcut(Actions::MakeOpenSceneAction(), [&]() { Commands::OpenSceneWithDialog(*m_context); });
Actions::HandleShortcut(Actions::MakeSaveSceneAction(), [&]() { Commands::SaveCurrentScene(*m_context); });
Actions::HandleShortcut(Actions::MakeSaveSceneAsAction(), [&]() { Commands::SaveSceneAsWithDialog(*m_context); });
Actions::HandleShortcut(Actions::MakeUndoAction(*m_context), [&]() { m_context->GetUndoManager().Undo(); });
Actions::HandleShortcut(Actions::MakeRedoAction(*m_context), [&]() { m_context->GetUndoManager().Redo(); });
const Actions::ShortcutContext shortcutContext = Actions::GlobalShortcutContext();
Actions::HandleShortcut(Actions::MakeNewSceneAction(), shortcutContext, [&]() { Commands::NewScene(*m_context); });
Actions::HandleShortcut(Actions::MakeOpenSceneAction(), shortcutContext, [&]() { Commands::OpenSceneWithDialog(*m_context); });
Actions::HandleShortcut(Actions::MakeSaveSceneAction(), shortcutContext, [&]() { Commands::SaveCurrentScene(*m_context); });
Actions::HandleShortcut(Actions::MakeSaveSceneAsAction(), shortcutContext, [&]() { Commands::SaveSceneAsWithDialog(*m_context); });
Actions::HandleShortcut(Actions::MakeUndoAction(*m_context), shortcutContext, [&]() { m_context->GetUndoManager().Undo(); });
Actions::HandleShortcut(Actions::MakeRedoAction(*m_context), shortcutContext, [&]() { m_context->GetUndoManager().Redo(); });
}
void MenuBar::ShowFileMenu() {
@@ -60,11 +65,29 @@ void MenuBar::ShowFileMenu() {
void MenuBar::ShowEditMenu() {
::XCEngine::Components::GameObject* selectedGameObject = Actions::GetSelectedGameObject(*m_context);
const AssetItemPtr selectedAssetItem = Actions::GetSelectedAssetItem(*m_context);
auto& projectManager = m_context->GetProjectManager();
const EditorActionRoute actionRoute = m_context->GetActiveActionRoute();
UI::DrawMenuScope("Edit", [&]() {
Actions::DrawMenuAction(Actions::MakeUndoAction(*m_context), [&]() { m_context->GetUndoManager().Undo(); });
Actions::DrawMenuAction(Actions::MakeRedoAction(*m_context), [&]() { m_context->GetUndoManager().Redo(); });
Actions::DrawMenuSeparator();
if (actionRoute == EditorActionRoute::Project) {
Actions::DrawMenuAction(Actions::MakeOpenAssetAction(Commands::CanOpenAsset(selectedAssetItem)), [&]() {
Commands::OpenAsset(*m_context, selectedAssetItem);
});
Actions::DrawMenuAction(Actions::MakeDeleteAssetAction(selectedAssetItem != nullptr), [&]() {
Commands::DeleteAsset(projectManager, projectManager.GetSelectedIndex());
});
Actions::DrawMenuSeparator();
Actions::DrawMenuAction(Actions::MakeNavigateBackAction(projectManager.CanNavigateBack()), [&]() {
projectManager.NavigateBack();
});
return;
}
Actions::DrawMenuAction(Actions::MakeCutAction(false), []() {});
Actions::DrawMenuAction(Actions::MakeCopyEntityAction(selectedGameObject), [&]() {
Commands::CopyEntity(*m_context, selectedGameObject->GetID());
@@ -72,6 +95,15 @@ void MenuBar::ShowEditMenu() {
Actions::DrawMenuAction(Actions::MakePasteEntityAction(*m_context), [&]() {
Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0);
});
Actions::DrawMenuAction(Actions::MakeDuplicateEntityAction(selectedGameObject), [&]() {
Commands::DuplicateEntity(*m_context, selectedGameObject->GetID());
});
Actions::DrawMenuAction(Actions::MakeDeleteEntityAction(selectedGameObject), [&]() {
Commands::DeleteEntity(*m_context, selectedGameObject->GetID());
});
Actions::DrawMenuAction(Actions::MakeRenameEntityAction(selectedGameObject), [&]() {
m_context->GetEventBus().Publish(EntityRenameRequestedEvent{ selectedGameObject->GetID() });
});
});
}

View File

@@ -1,3 +1,4 @@
#include "Actions/ActionRouting.h"
#include "Actions/EditorActions.h"
#include "Commands/ProjectCommands.h"
#include "ProjectPanel.h"
@@ -32,6 +33,9 @@ void ProjectPanel::Render() {
if (!panel.IsOpen()) {
return;
}
Actions::ObserveFocusedActionRoute(*m_context, EditorActionRoute::Project);
HandleKeyboardShortcuts();
auto& manager = m_context->GetProjectManager();
@@ -146,6 +150,30 @@ void ProjectPanel::Render() {
}
}
AssetItemPtr ProjectPanel::GetSelectedItem() const {
return m_context ? Actions::GetSelectedAssetItem(*m_context) : nullptr;
}
void ProjectPanel::HandleKeyboardShortcuts() {
if (!m_context) {
return;
}
auto& manager = m_context->GetProjectManager();
const AssetItemPtr selectedItem = GetSelectedItem();
const Actions::ShortcutContext shortcutContext = Actions::FocusedWindowShortcutContext();
Actions::HandleShortcut(Actions::MakeNavigateBackAction(manager.CanNavigateBack()), shortcutContext, [&]() {
manager.NavigateBack();
});
Actions::HandleShortcut(Actions::MakeOpenAssetAction(Commands::CanOpenAsset(selectedItem)), shortcutContext, [&]() {
Commands::OpenAsset(*m_context, selectedItem);
});
Actions::HandleShortcut(Actions::MakeDeleteAssetAction(selectedItem != nullptr), shortcutContext, [&]() {
Commands::DeleteAsset(manager, manager.GetSelectedIndex());
});
}
void ProjectPanel::RenderAssetItem(const AssetItemPtr& item, int index) {
auto& manager = m_context->GetProjectManager();
bool isSelected = (manager.GetSelectedIndex() == index);

View File

@@ -14,6 +14,8 @@ public:
void Initialize(const std::string& projectPath);
private:
AssetItemPtr GetSelectedItem() const;
void HandleKeyboardShortcuts();
void RenderAssetItem(const AssetItemPtr& item, int index);
char m_searchBuffer[256] = "";