#include "Actions/ActionRouting.h" #include "Actions/EditActionRouter.h" #include "Actions/MainMenuActionRouter.h" #include "MenuBar.h" #include "Core/IEditorContext.h" #include "Platform/Win32Utf8.h" #include "UI/UI.h" #include #include #include #include #include namespace XCEngine { namespace Editor { namespace { constexpr float kRunToolbarHeight = 30.0f; constexpr float kRunToolbarButtonExtent = 26.0f; constexpr float kRunToolbarButtonSpacing = 8.0f; constexpr float kRunToolbarIconInset = 4.0f; std::string BuildRunToolbarIconPath(const char* fileName) { const std::filesystem::path exeDir( Platform::Utf8ToWide(Platform::GetExecutableDirectoryUtf8())); const std::filesystem::path iconPath = (exeDir / L".." / L".." / L"resources" / L"Icons" / std::filesystem::path(Platform::Utf8ToWide(fileName))) .lexically_normal(); return Platform::WideToUtf8(iconPath.wstring()); } const std::string& GetRunToolbarIconPath(size_t index) { static const std::array kFileNames = { "play_button.png", "pause_button.png", "step_button.png" }; static std::array s_cachedPaths = {}; std::string& cachedPath = s_cachedPaths[index]; if (!cachedPath.empty()) { return cachedPath; } cachedPath = BuildRunToolbarIconPath(kFileNames[index]); return cachedPath; } bool DrawRunToolbarIconButton( const char* id, const Actions::ActionBinding& action, const std::string& iconPath, const char* fallbackGlyph) { ImGui::BeginDisabled(!action.enabled); ImGui::PushID(id); const ImVec2 buttonSize(kRunToolbarButtonExtent, kRunToolbarButtonExtent); const ImVec2 buttonMin = ImGui::GetCursorScreenPos(); const bool pressed = ImGui::InvisibleButton("##RunToolbarButton", buttonSize); const ImVec2 buttonMax(buttonMin.x + buttonSize.x, buttonMin.y + buttonSize.y); const bool hovered = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled); const bool held = ImGui::IsItemActive(); ImDrawList* drawList = ImGui::GetWindowDrawList(); if (drawList != nullptr) { const ImU32 fillColor = ImGui::GetColorU32( held ? UI::ToolbarButtonActiveColor() : hovered ? UI::ToolbarButtonHoveredColor(action.selected) : UI::ToolbarButtonColor(action.selected)); const ImU32 borderColor = IM_COL32(255, 255, 255, action.selected ? 56 : 28); drawList->AddRectFilled(buttonMin, buttonMax, fillColor, 5.0f); drawList->AddRect(buttonMin, buttonMax, borderColor, 5.0f, 0, 1.0f); const ImVec2 iconMin(buttonMin.x + kRunToolbarIconInset, buttonMin.y + kRunToolbarIconInset); const ImVec2 iconMax(buttonMax.x - kRunToolbarIconInset, buttonMax.y - kRunToolbarIconInset); if (!UI::DrawTextureAssetPreview(drawList, iconMin, iconMax, iconPath)) { const ImVec2 textSize = ImGui::CalcTextSize(fallbackGlyph); drawList->AddText( ImVec2( buttonMin.x + (buttonSize.x - textSize.x) * 0.5f, buttonMin.y + (buttonSize.y - textSize.y) * 0.5f), ImGui::GetColorU32(ImVec4(0.88f, 0.88f, 0.88f, 1.0f)), fallbackGlyph); } } ImGui::PopID(); ImGui::EndDisabled(); return action.enabled && pressed; } } // namespace MenuBar::MenuBar() : Panel("MenuBar") {} void MenuBar::Render() { RenderChrome(); RenderOverlays(); } void MenuBar::RenderChrome() { if (!m_context) { return; } Actions::HandleMenuBarShortcuts(*m_context); Actions::DrawMainMenuBar(*m_context, m_aboutPopup); RenderRunToolbar(); } void MenuBar::RenderOverlays() { if (!m_context) { return; } Actions::DrawMainMenuOverlays(m_context, m_aboutPopup); } void MenuBar::RenderRunToolbar() { ImGuiViewport* viewport = ImGui::GetMainViewport(); if (viewport == nullptr) { return; } constexpr ImGuiWindowFlags kWindowFlags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNavFocus; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.0f, 2.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleColor(ImGuiCol_WindowBg, UI::ToolbarBackgroundColor()); const bool open = ImGui::BeginViewportSideBar("##MainRunToolbar", viewport, ImGuiDir_Up, kRunToolbarHeight, kWindowFlags); ImGui::PopStyleColor(); ImGui::PopStyleVar(3); if (open) { const Actions::ActionBinding playAction = Actions::MakeTogglePlayModeAction( m_context->GetRuntimeMode(), m_context->GetRuntimeMode() != EditorRuntimeMode::Edit || m_context->GetSceneManager().HasActiveScene()); const bool canPause = m_context->GetRuntimeMode() == EditorRuntimeMode::Play || m_context->GetRuntimeMode() == EditorRuntimeMode::Paused; const Actions::ActionBinding pauseAction = Actions::MakeTogglePauseModeAction(m_context->GetRuntimeMode(), canPause); const Actions::ActionBinding stepAction = Actions::MakeStepPlayModeAction(m_context->GetRuntimeMode() == EditorRuntimeMode::Paused); const float totalWidth = kRunToolbarButtonExtent * 3.0f + kRunToolbarButtonSpacing * 2.0f; const float startX = ImGui::GetCursorPosX() + (std::max)(0.0f, (ImGui::GetContentRegionAvail().x - totalWidth) * 0.5f); const float startY = (std::max)(ImGui::GetCursorPosY(), (ImGui::GetWindowHeight() - kRunToolbarButtonExtent) * 0.5f); ImGui::SetCursorPos(ImVec2(startX, startY)); if (DrawRunToolbarIconButton( "Play", playAction, GetRunToolbarIconPath(0), "P")) { Actions::RequestTogglePlayMode(*m_context); } ImGui::SameLine(0.0f, kRunToolbarButtonSpacing); if (DrawRunToolbarIconButton( "Pause", pauseAction, GetRunToolbarIconPath(1), "||")) { Actions::RequestTogglePauseMode(*m_context); } ImGui::SameLine(0.0f, kRunToolbarButtonSpacing); if (DrawRunToolbarIconButton( "Step", stepAction, GetRunToolbarIconPath(2), ">")) { Actions::RequestStepPlayMode(*m_context); } } ImGui::End(); } } }