diff --git a/Core/Source/Lux/Core/Window.cpp b/Core/Source/Lux/Core/Window.cpp index 44a5adb..2f04713 100644 --- a/Core/Source/Lux/Core/Window.cpp +++ b/Core/Source/Lux/Core/Window.cpp @@ -155,13 +155,8 @@ namespace Lux { if (!m_Specification.Decorated) { - // This removes titlebar on all platforms - // and all of the native window effects on non-Windows platforms -#ifdef LUX_PLATFORM_WINDOWS - glfwWindowHint(GLFW_TITLEBAR, false); -#else + // Disable native decorations so editor can render a fully custom titlebar. glfwWindowHint(GLFW_DECORATED, false); -#endif } m_WindowHandle = glfwCreateWindow((int)m_Specification.Width, (int)m_Specification.Height, m_Data.Title.c_str(), m_Specification.Fullscreen ? glfwGetPrimaryMonitor() : nullptr, nullptr); diff --git a/Core/Source/Lux/ImGui/ImGuiEx.h b/Core/Source/Lux/ImGui/ImGuiEx.h index 770c79c..d961c70 100644 --- a/Core/Source/Lux/ImGui/ImGuiEx.h +++ b/Core/Source/Lux/ImGui/ImGuiEx.h @@ -12,6 +12,7 @@ #include "Lux/ImGui/ImGuiWidgets.h" //#include "Lux/Scene/Prefab.h" #include "Lux/Scene/Scene.h" +#include "Lux/UI/UI.h" #include "Lux/Utilities/StringUtils.h" #include @@ -2802,7 +2803,7 @@ namespace Lux::ImGuiEx { uintptr_t tempLength = length; FieldType nativeType = arrayStorage->GetFieldInfo()->Type; - if (UI::PropertyInput("Length", tempLength, 1, 1, ImGuiInputTextFlags_EnterReturnsTrue)) + if (PropertyInput("Length", tempLength, 1, 1, ImGuiInputTextFlags_EnterReturnsTrue)) { arrayStorage->Resize((uint32_t)tempLength); length = tempLength; @@ -2827,12 +2828,12 @@ namespace Lux::ImGuiEx { size_t dataSize = ImGui::DataTypeGetInfo(dataType)->Size; if (components > 1) { - if (UI::DragScalarN(GenerateID(), dataType, &data, components, 1.0f, (const void*)0, (const void*)0, format, 0)) + if (DragScalarN(GenerateID(), dataType, &data, components, 1.0f, (const void*)0, (const void*)0, format, 0)) arrayStorage->SetValue>((uint32_t)index, data); } else { - if (UI::DragScalar(GenerateID(), dataType, &data, 1.0f, (const void*)0, (const void*)0, format, (ImGuiSliderFlags)0)) + if (DragScalar(GenerateID(), dataType, &data, 1.0f, (const void*)0, (const void*)0, format, (ImGuiSliderFlags)0)) arrayStorage->SetValue>((uint32_t)index, data); } @@ -2863,7 +2864,7 @@ namespace Lux::ImGuiEx { const float buttonSize = ImGui::GetFrameHeight(); ImGui::SetNextItemWidth(ImMax(1.0f, ImGui::CalcItemWidth() - (buttonSize + style.ItemInnerSpacing.x))); - if (UI::InputText(GenerateID(), &data)) + if (InputText(GenerateID(), &data)) arrayStorage->SetValue((uint32_t)index, data); const ImVec2 backupFramePadding = style.FramePadding; @@ -2892,7 +2893,7 @@ namespace Lux::ImGuiEx { case FieldType::Bool: { bool value = arrayStorage->GetValue(i); - if (UI::Property(indexString.c_str(), value)) + if (Property(indexString.c_str(), value)) { arrayStorage->SetValue(i, value); modified = true; diff --git a/Core/Source/Lux/UI/UI.h b/Core/Source/Lux/UI/UI.h index 86dacc8..e182a74 100644 --- a/Core/Source/Lux/UI/UI.h +++ b/Core/Source/Lux/UI/UI.h @@ -2,34 +2,144 @@ #include +#ifndef IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_MATH_OPERATORS +#endif +#include + +#include "Lux/ImGui/Colors.h" +#include "Lux/ImGui/ImGuiUtilities.h" + namespace Lux::UI { - struct ScopedStyleColor + // ========================================================================= + // Scoped helpers — mirrors the Hazel UI:: namespace conventions + // ========================================================================= + + /// RAII wrapper for ImGui::PushStyleColor / PopStyleColor + struct ScopedColour { - ScopedStyleColor() = default; + ScopedColour() = default; + + ScopedColour(ImGuiCol idx, ImVec4 colour, bool predicate = true) + : m_Set(predicate) + { + if (predicate) + ImGui::PushStyleColor(idx, colour); + } - ScopedStyleColor(ImGuiCol idx, ImVec4 color, bool predicate = true) + ScopedColour(ImGuiCol idx, ImU32 colour, bool predicate = true) : m_Set(predicate) { if (predicate) - ImGui::PushStyleColor(idx, color); + ImGui::PushStyleColor(idx, colour); } - ScopedStyleColor(ImGuiCol idx, ImU32 color, bool predicate = true) + ScopedColour(ImGuiCol idx, ImColor colour, bool predicate = true) : m_Set(predicate) { if (predicate) - ImGui::PushStyleColor(idx, color); + ImGui::PushStyleColor(idx, colour.Value); } - ~ScopedStyleColor() + ~ScopedColour() { if (m_Set) ImGui::PopStyleColor(); } + + ScopedColour(const ScopedColour&) = delete; + ScopedColour& operator=(const ScopedColour&) = delete; private: bool m_Set = false; }; + // Legacy alias kept for backwards compatibility + using ScopedStyleColor = ScopedColour; + + /// RAII wrapper for ImGui::PushStyleVar / PopStyleVar + struct ScopedStyle + { + ScopedStyle(ImGuiStyleVar styleVar, float value) { ImGui::PushStyleVar(styleVar, value); } + ScopedStyle(ImGuiStyleVar styleVar, ImVec2 value) { ImGui::PushStyleVar(styleVar, value); } + ~ScopedStyle() { ImGui::PopStyleVar(); } + + ScopedStyle(const ScopedStyle&) = delete; + ScopedStyle& operator=(const ScopedStyle&) = delete; + }; + + /// RAII wrapper for ImGui::PushFont / PopFont + struct ScopedFont + { + ScopedFont(ImFont* font) { ImGui::PushFont(font); } + ~ScopedFont() { ImGui::PopFont(); } + + ScopedFont(const ScopedFont&) = delete; + ScopedFont& operator=(const ScopedFont&) = delete; + }; + + // ========================================================================= + // Cursor helpers + // ========================================================================= + + /// Shift the cursor position along the X axis by the given amount + inline void ShiftCursorX(float distance) + { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + distance); + } + + /// Shift the cursor position along the Y axis by the given amount + inline void ShiftCursorY(float distance) + { + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + distance); + } + + /// Shift the cursor position along both axes + inline void ShiftCursor(float x, float y) + { + const ImVec2 cursor = ImGui::GetCursorPos(); + ImGui::SetCursorPos(ImVec2(cursor.x + x, cursor.y + y)); + } + + // ========================================================================= + // DrawButtonImage — draws a texture directly into an already-placed item + // rect, choosing the tint based on the item interaction state. + // ========================================================================= + + /// Draw a button image using separate textures for each interaction state. + /// Call this *after* an invisible button or any other item that occupies the + /// desired rect, then the tint colour is picked automatically. + inline void DrawButtonImage( + const Ref& imageNormal, + const Ref& imageHovered, + const Ref& imagePressed, + ImU32 tintNormal, ImU32 tintHovered, ImU32 tintPressed, + ImVec2 rectMin, ImVec2 rectMax, + ImVec2 uv0 = ImVec2(0.0f, 0.0f), ImVec2 uv1 = ImVec2(1.0f, 1.0f)) + { + // Delegate to ImGuiEx which has the full implementation + ImGuiEx::DrawButtonImage(imageNormal, imageHovered, imagePressed, + tintNormal, tintHovered, tintPressed, rectMin, rectMax, uv0, uv1); + } + + /// Convenience overload — single texture for all states + inline void DrawButtonImage( + const Ref& image, + ImU32 tintNormal, ImU32 tintHovered, ImU32 tintPressed, + ImVec2 rectMin, ImVec2 rectMax, + ImVec2 uv0 = ImVec2(0.0f, 0.0f), ImVec2 uv1 = ImVec2(1.0f, 1.0f)) + { + ImGuiEx::DrawButtonImage(image, tintNormal, tintHovered, tintPressed, + rectMin, rectMax, uv0, uv1); + } + + /// Convenience overload — uses the last item rect for the image bounds + inline void DrawButtonImage( + const Ref& image, + ImU32 tintNormal, ImU32 tintHovered, ImU32 tintPressed, + ImVec2 uv0 = ImVec2(0.0f, 0.0f), ImVec2 uv1 = ImVec2(1.0f, 1.0f)) + { + ImGuiEx::DrawButtonImage(image, tintNormal, tintHovered, tintPressed, uv0, uv1); + } } diff --git a/Editor/Source/EditorLayer.cpp b/Editor/Source/EditorLayer.cpp index 3687426..32bb475 100644 --- a/Editor/Source/EditorLayer.cpp +++ b/Editor/Source/EditorLayer.cpp @@ -17,6 +17,7 @@ #include #include "imgui/imgui_internal.h" +#include #include "ImGuizmo.h" #include "Lux/Debug/Profiler.h" #include "Lux/Editor/EditorResources.h" @@ -66,6 +67,24 @@ namespace Lux { auto* imguiRenderer = Lux::Application::Get().GetImGuiLayer()->GetImGuiRenderer(); return imguiRenderer->CreateFrameTexture(image->GetHandle().Get(), nvrhi::AllSubresources); } + + std::string GetSceneDisplayName(const std::filesystem::path& scenePath) + { + if (scenePath.empty()) + return "Untitled Scene"; + + return scenePath.stem().string(); + } + + std::string GetProjectDisplayName() + { + Ref activeProject = Project::GetActive(); + if (!activeProject) + return "No Project"; + + const auto& name = activeProject->GetConfig().Name; + return name.empty() ? "Untitled Project" : name; + } } struct SceneRendererRuntimeState @@ -126,6 +145,8 @@ namespace Lux { { LUX_PROFILE_FUNCTION("EditorLayer::OnAttach"); + EditorResources::Init(); + /////////// Configure Panels /////////// m_PanelManager = CreateScope(); @@ -217,6 +238,7 @@ namespace Lux { m_SceneRendererPanel.reset(); m_SceneHierarchyPanel.reset(); s_Font.reset(); + EditorResources::Shutdown(); } void EditorLayer::OnUpdate(Timestep ts) @@ -313,7 +335,10 @@ namespace Lux { bool opt_fullscreen = opt_fullscreen_persistent; static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; - ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + GLFWwindow* nativeWindow = Application::Get().GetWindow().GetNativeWindow(); + const bool isWindowMaximized = nativeWindow && glfwGetWindowAttrib(nativeWindow, GLFW_MAXIMIZED); + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking; if (opt_fullscreen) { ImGuiViewport* viewport = ImGui::GetMainViewport(); @@ -321,7 +346,7 @@ namespace Lux { ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, isWindowMaximized ? 0.0f : 3.0f); window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; @@ -343,6 +368,15 @@ namespace Lux { ImGuiStyle& style = ImGui::GetStyle(); const float minWinSizeX = style.WindowMinSize.x; style.WindowMinSize.x = 370.0f; + + UI_DrawTitlebar(); + if (!isWindowMaximized) + { + ImGuiEx::ScopedColour borderColour(ImGuiCol_Border, IM_COL32(50, 50, 50, 255)); + ImGuiEx::RenderWindowOuterBorders(ImGui::GetCurrentWindow()); + } + + ImGui::SetCursorPosY(m_TitlebarHeight); if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { const ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); @@ -350,45 +384,32 @@ namespace Lux { } style.WindowMinSize.x = minWinSizeX; - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - if (ImGui::MenuItem("Open Project...", "Ctrl+O")) - OpenProject(); - - ImGui::Separator(); - - if (ImGui::MenuItem("New Scene", "Ctrl+N")) - NewScene(); - - if (ImGui::MenuItem("Save Scene", "Ctrl+S")) - SaveScene(); - - if (ImGui::MenuItem("Save Scene As...", "Ctrl+Shift+S")) - SaveSceneAs(); - - ImGui::Separator(); + m_PanelManager->OnImGuiRender(); - if (ImGui::MenuItem("Exit")) - Application::Get().Close(); + if (m_ShowImGuiMetrics) + ImGui::ShowMetricsWindow(&m_ShowImGuiMetrics); + if (m_ShowImGuiStyleEditor) + ImGui::ShowStyleEditor(); + if (m_ShowAboutPopup) + ImGui::OpenPopup("About LuxEngine"); - ImGui::EndMenu(); - } + if (ImGui::BeginPopupModal("About LuxEngine", &m_ShowAboutPopup, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("LuxEngine Editor"); + ImGui::Separator(); + ImGui::Text("Version: %s", Application::GetConfigurationName()); + ImGui::Text("Platform: %s", Application::GetPlatformName()); + ImGui::TextWrapped("Credits: Inspired by Hazel architecture and editor workflows."); - if (ImGui::BeginMenu("Script")) + if (ImGui::Button("Close")) { - if (ImGui::MenuItem("Reload assembly", "Ctrl+R")) - ScriptEngine::ReloadAssembly(); - - ImGui::EndMenu(); + m_ShowAboutPopup = false; + ImGui::CloseCurrentPopup(); } - ImGui::EndMenuBar(); + ImGui::EndPopup(); } - m_PanelManager->OnImGuiRender(); - // Stats panel ImGui::Begin("Stats"); @@ -522,6 +543,193 @@ namespace Lux { ImGui::End(); // Lux Editor } + void EditorLayer::UI_DrawMenubar() + { + ImGuiEx::ScopedColourStack menuColors( + ImGuiCol_Header, ImGui::ColorConvertU32ToFloat4(Colors::Theme::accent), + ImGuiCol_HeaderHovered, ImGui::ColorConvertU32ToFloat4(Colors::Theme::accent), + ImGuiCol_HeaderActive, ImGui::ColorConvertU32ToFloat4(Colors::Theme::accent), + ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(Colors::Theme::text)); + + const float menuMinX = 76.0f; + const float menuMaxX = std::max(menuMinX + 1.0f, ImGui::GetWindowWidth() - 350.0f); + const ImRect menuBarRect(ImVec2(menuMinX, 0.0f), ImVec2(menuMaxX, m_TitlebarHeight)); + if (!ImGuiEx::BeginMenuBar(menuBarRect)) + return; + + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Create Project")) + NewProject(); + if (ImGui::MenuItem("Open Project...", "Ctrl+O")) + OpenProject(); + + if (ImGui::BeginMenu("Recent Projects")) + { + ImGui::MenuItem("No recent projects", nullptr, false, false); + ImGui::EndMenu(); + } + + ImGui::Separator(); + if (ImGui::MenuItem("Save Scene", "Ctrl+S")) + SaveScene(); + if (ImGui::MenuItem("Save Scene As...", "Ctrl+Shift+S")) + SaveSceneAs(); + + ImGui::Separator(); + if (ImGui::MenuItem("Exit")) + Application::Get().DispatchEvent(); + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Edit")) + { + if (ImGui::MenuItem("Reload C# Assembly", "Ctrl+R")) + ScriptEngine::ReloadAssembly(); + ImGui::MenuItem("Second Viewport", nullptr, &m_SecondViewportEnabled); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("View")) + { + auto& viewPanels = m_PanelManager->GetPanels(PanelCategory::View); + for (auto& [id, panelData] : viewPanels) + ImGui::MenuItem(panelData.Name, nullptr, &panelData.IsOpen); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Tools")) + { + ImGui::MenuItem("ImGui Metrics", nullptr, &m_ShowImGuiMetrics); + ImGui::MenuItem("ImGui Style Editor", nullptr, &m_ShowImGuiStyleEditor); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Help")) + { + if (ImGui::MenuItem("About")) + m_ShowAboutPopup = true; + ImGui::EndMenu(); + } + + ImGuiEx::EndMenuBar(); + } + + void EditorLayer::UI_DrawTitlebar() + { + ImGuiWindow* window = ImGui::GetCurrentWindow(); + ImDrawList* drawList = ImGui::GetWindowDrawList(); + const ImVec2 windowPos = window->Pos; + const ImVec2 windowMax = ImVec2(window->Pos.x + window->Size.x, window->Pos.y + m_TitlebarHeight); + + ImU32 targetTitlebarColor = Colors::Theme::titlebar; + if (m_SceneState == SceneState::Play) + targetTitlebarColor = Colors::Theme::titlebarOrange; + else if (m_SceneState == SceneState::Simulate) + targetTitlebarColor = Colors::Theme::titlebarGreen; + + const ImVec4 targetColor = ImGui::ColorConvertU32ToFloat4(targetTitlebarColor); + const float dt = ImGui::GetIO().DeltaTime; + m_AnimatedTitlebarColor = ImLerp(m_AnimatedTitlebarColor, targetColor, std::clamp(dt * 8.0f, 0.0f, 1.0f)); + drawList->AddRectFilled(windowPos, windowMax, ImGui::ColorConvertFloat4ToU32(m_AnimatedTitlebarColor)); + + drawList->AddLine(ImVec2(windowPos.x, windowPos.y + m_TitlebarHeight), ImVec2(windowPos.x + window->Size.x, windowPos.y + m_TitlebarHeight), Colors::Theme::backgroundDark); + + const float logoPadding = 16.0f; + const float logoSize = 30.0f; + const float logoTop = (m_TitlebarHeight - logoSize) * 0.5f; + const ImVec2 logoMin(windowPos.x + logoPadding, windowPos.y + logoTop); + const ImVec2 logoMax(logoMin.x + logoSize, logoMin.y + logoSize); + if (EditorResources::HazelLogoTexture) + drawList->AddImage(GetImGuiTextureID(EditorResources::HazelLogoTexture), logoMin, logoMax); + + UI_DrawMenubar(); + + const std::string sceneName = GetSceneDisplayName(m_EditorScenePath); + const ImVec2 sceneNameSize = ImGui::CalcTextSize(sceneName.c_str()); + const float sceneNameX = windowPos.x + (window->Size.x - sceneNameSize.x) * 0.5f; + const float sceneNameY = windowPos.y + (m_TitlebarHeight - sceneNameSize.y) * 0.5f; + drawList->AddText(ImVec2(sceneNameX, sceneNameY), Colors::Theme::textBrighter, sceneName.c_str()); + drawList->AddLine( + ImVec2(sceneNameX - 6.0f, sceneNameY + sceneNameSize.y + 4.0f), + ImVec2(sceneNameX + sceneNameSize.x + 6.0f, sceneNameY + sceneNameSize.y + 4.0f), + Colors::Theme::accent, 1.5f); + + GLFWwindow* nativeWindow = Application::Get().GetWindow().GetNativeWindow(); + const bool isMaximized = nativeWindow && glfwGetWindowAttrib(nativeWindow, GLFW_MAXIMIZED); + + const float controlsWidth = 120.0f; + const float dragZoneMinX = 70.0f; + const float dragZoneMaxX = window->Size.x - controlsWidth - 220.0f; + ImGui::SetCursorPos(ImVec2(dragZoneMinX, 0.0f)); + ImGui::InvisibleButton("##titleBarDragZone", ImVec2(std::max(0.0f, dragZoneMaxX - dragZoneMinX), m_TitlebarHeight)); + const ImVec2 dragMin = ImGui::GetItemRectMin(); + const ImVec2 dragMax = ImGui::GetItemRectMax(); + m_TitleBarDragRectMin = ImVec2(dragMin.x - windowPos.x, dragMin.y - windowPos.y); + m_TitleBarDragRectMax = ImVec2(dragMax.x - windowPos.x, dragMax.y - windowPos.y); + +#if !defined(LUX_PLATFORM_WINDOWS) + if (nativeWindow && !isMaximized && ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) + { + int windowX = 0, windowY = 0; + glfwGetWindowPos(nativeWindow, &windowX, &windowY); + const ImVec2 delta = ImGui::GetIO().MouseDelta; + glfwSetWindowPos(nativeWindow, windowX + (int)delta.x, windowY + (int)delta.y); + } +#endif + + const std::string projectName = GetProjectDisplayName(); + const ImVec2 projectNameSize = ImGui::CalcTextSize(projectName.c_str()); + const ImVec2 projectBoxMin(windowPos.x + window->Size.x - controlsWidth - projectNameSize.x - 36.0f, windowPos.y + 14.0f); + const ImVec2 projectBoxMax(projectBoxMin.x + projectNameSize.x + 20.0f, projectBoxMin.y + 26.0f); + drawList->AddRect(projectBoxMin, projectBoxMax, Colors::Theme::muted, 6.0f, 0, 1.0f); + drawList->AddText(ImVec2(projectBoxMin.x + 10.0f, projectBoxMin.y + 5.0f), Colors::Theme::text, projectName.c_str()); + + const float buttonSize = 34.0f; + const float buttonY = (m_TitlebarHeight - buttonSize) * 0.5f; + const float buttonsStartX = window->Size.x - controlsWidth; + const ImU32 normalTint = IM_COL32(220, 220, 220, 220); + const ImU32 hoverTint = IM_COL32(255, 255, 255, 255); + const ImU32 activeTint = IM_COL32(200, 200, 200, 255); + + auto drawWindowControlButton = [&](const char* id, const Ref& icon, float localX, auto&& onClick) + { + ImGui::SetCursorPos(ImVec2(localX, buttonY)); + ImGui::InvisibleButton(id, ImVec2(buttonSize, buttonSize)); + if (icon) + UI::DrawButtonImage(icon, normalTint, hoverTint, activeTint, ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) + onClick(); + }; + + drawWindowControlButton("##minimizeWindow", EditorResources::MinimizeIcon, buttonsStartX + 4.0f, [nativeWindow]() + { + if (!nativeWindow) + return; + Application::Get().QueueEvent([nativeWindow]() { glfwIconifyWindow(nativeWindow); }); + }); + + drawWindowControlButton("##maximizeRestoreWindow", isMaximized ? EditorResources::RestoreIcon : EditorResources::MaximizeIcon, buttonsStartX + 42.0f, [nativeWindow, isMaximized]() + { + if (!nativeWindow) + return; + Application::Get().QueueEvent([nativeWindow, isMaximized]() + { + if (isMaximized) + glfwRestoreWindow(nativeWindow); + else + glfwMaximizeWindow(nativeWindow); + }); + }); + + drawWindowControlButton("##closeWindow", EditorResources::CloseIcon, buttonsStartX + 80.0f, []() + { + Application::Get().DispatchEvent(); + }); + } + void EditorLayer::UI_Toolbar() { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 2)); @@ -633,6 +841,7 @@ namespace Lux { EventDispatcher dispatcher(e); dispatcher.Dispatch(LUX_BIND_EVENT_FN(EditorLayer::OnKeyPressed)); dispatcher.Dispatch(LUX_BIND_EVENT_FN(EditorLayer::OnMouseButtonPressed)); + dispatcher.Dispatch(LUX_BIND_EVENT_FN(EditorLayer::OnTitleBarHitTest)); } bool EditorLayer::OnKeyPressed(KeyPressedEvent& e) @@ -678,6 +887,20 @@ namespace Lux { return false; } + bool EditorLayer::OnTitleBarHitTest(WindowTitleBarHitTestEvent& e) + { + const float x = (float)e.GetX(); + const float y = (float)e.GetY(); + + const bool inDragZone = x >= m_TitleBarDragRectMin.x && x <= m_TitleBarDragRectMax.x + && y >= m_TitleBarDragRectMin.y && y <= m_TitleBarDragRectMax.y; + + if (inDragZone) + e.SetHit(true); + + return inDragZone; + } + void EditorLayer::OnOverlayRender() { if (m_SceneState == SceneState::Play) diff --git a/Editor/Source/EditorLayer.h b/Editor/Source/EditorLayer.h index a17041b..7c0932c 100644 --- a/Editor/Source/EditorLayer.h +++ b/Editor/Source/EditorLayer.h @@ -40,9 +40,12 @@ namespace Lux private: bool OnKeyPressed(KeyPressedEvent& e); bool OnMouseButtonPressed(MouseButtonPressedEvent& e); + bool OnTitleBarHitTest(WindowTitleBarHitTestEvent& e); //bool OnWindowDrop(WindowDropEvent& e); void OnOverlayRender(); + void UI_DrawTitlebar(); + void UI_DrawMenubar(); void NewProject(); bool OpenProject(); @@ -113,6 +116,16 @@ namespace Lux }; SceneState m_SceneState = SceneState::Edit; + ImVec4 m_AnimatedTitlebarColor = ImGui::ColorConvertU32ToFloat4(Colors::Theme::titlebar); + ImVec2 m_TitleBarDragRectMin = { 0.0f, 0.0f }; + ImVec2 m_TitleBarDragRectMax = { 0.0f, 0.0f }; + float m_TitlebarHeight = 57.0f; + + bool m_ShowImGuiMetrics = false; + bool m_ShowImGuiStyleEditor = false; + bool m_ShowAboutPopup = false; + bool m_SecondViewportEnabled = false; + // Editor resources Ref m_IconPlay, m_IconPause, m_IconStep, m_IconSimulate, m_IconStop; };