Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement window redrawing during resize #82

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 132 additions & 115 deletions Walnut/Platform/GUI/Walnut/ApplicationGUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,17 @@ static void glfw_error_callback(int error, const char* description)
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}

static void Walnut_WindowSizeCallback(GLFWwindow* window, int width, int height)
{
// https://github.com/ocornut/imgui/pull/6461/files
auto& app = Walnut::Application::Get();
if (window == app.GetWindowHandle())
{
g_SwapChainRebuild = true;
app.Frame();
}
}

namespace Walnut {

#include "Walnut/Embed/Walnut-Icon.embed"
Expand Down Expand Up @@ -629,6 +640,8 @@ namespace Walnut {
free(data);
}

// Now the window is initialized, we are ready to redraw in resize events
glfwSetWindowSizeCallback(m_WindowHandle, Walnut_WindowSizeCallback);
}

void Application::Shutdown()
Expand Down Expand Up @@ -852,153 +865,157 @@ namespace Walnut {
{
m_Running = true;

ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
ImGuiIO& io = ImGui::GetIO();

// Main loop
while (!glfwWindowShouldClose(m_WindowHandle) && m_Running)
{
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents();

{
std::scoped_lock<std::mutex> lock(m_EventQueueMutex);
Frame();
if (m_MainMinimized)
std::this_thread::sleep_for(std::chrono::milliseconds(5));

// Process custom event queue
while (m_EventQueue.size() > 0)
{
auto& func = m_EventQueue.front();
func();
m_EventQueue.pop();
}
}
}
}

for (auto& layer : m_LayerStack)
layer->OnUpdate(m_TimeStep);
void Application::Frame()
{
ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

// Resize swap chain?
if (g_SwapChainRebuild)
{
int width, height;
glfwGetFramebufferSize(m_WindowHandle, &width, &height);
if (width > 0 && height > 0)
{
ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
g_MainWindowData.FrameIndex = 0;
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents();

// Clear allocated command buffers from here since entire pool is destroyed
s_AllocatedCommandBuffers.clear();
s_AllocatedCommandBuffers.resize(g_MainWindowData.ImageCount);
{
std::scoped_lock<std::mutex> lock(m_EventQueueMutex);

g_SwapChainRebuild = false;
}
// Process custom event queue
while (m_EventQueue.size() > 0)
{
auto& func = m_EventQueue.front();
func();
m_EventQueue.pop();
}
}

// Start the Dear ImGui frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
for (auto& layer : m_LayerStack)
layer->OnUpdate(m_TimeStep);

// Resize swap chain?
if (g_SwapChainRebuild)
{
int width, height;
glfwGetFramebufferSize(m_WindowHandle, &width, &height);
if (width > 0 && height > 0)
{
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
// because it would be confusing to have two docking targets within each others.
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking;
ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
g_MainWindowData.FrameIndex = 0;

ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
if (!m_Specification.CustomTitlebar && m_MenubarCallback)
window_flags |= ImGuiWindowFlags_MenuBar;
// Clear allocated command buffers from here since entire pool is destroyed
s_AllocatedCommandBuffers.clear();
s_AllocatedCommandBuffers.resize(g_MainWindowData.ImageCount);

const bool isMaximized = IsMaximized();

ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, isMaximized ? ImVec2(6.0f, 6.0f) : ImVec2(1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 3.0f);

ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4{ 0.0f, 0.0f, 0.0f, 0.0f });
ImGui::Begin("DockSpaceWindow", nullptr, window_flags);
ImGui::PopStyleColor(); // MenuBarBg
ImGui::PopStyleVar(2);
g_SwapChainRebuild = false;
}
}

ImGui::PopStyleVar(2);
// Start the Dear ImGui frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();

{
ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(50, 50, 50, 255));
// Draw window border if the window is not maximized
if (!isMaximized)
UI::RenderWindowOuterBorders(ImGui::GetCurrentWindow());
{
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
// because it would be confusing to have two docking targets within each others.
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking;

ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
if (!m_Specification.CustomTitlebar && m_MenubarCallback)
window_flags |= ImGuiWindowFlags_MenuBar;

ImGui::PopStyleColor(); // ImGuiCol_Border
}

if (m_Specification.CustomTitlebar)
{
float titleBarHeight;
UI_DrawTitlebar(titleBarHeight);
ImGui::SetCursorPosY(titleBarHeight);
const bool isMaximized = IsMaximized();

}
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, isMaximized ? ImVec2(6.0f, 6.0f) : ImVec2(1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 3.0f);

// Dockspace
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
float minWinSizeX = style.WindowMinSize.x;
style.WindowMinSize.x = 370.0f;
ImGui::DockSpace(ImGui::GetID("MyDockspace"));
style.WindowMinSize.x = minWinSizeX;
ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4{ 0.0f, 0.0f, 0.0f, 0.0f });
ImGui::Begin("DockSpaceWindow", nullptr, window_flags);
ImGui::PopStyleColor(); // MenuBarBg
ImGui::PopStyleVar(2);

if (!m_Specification.CustomTitlebar)
UI_DrawMenubar();
ImGui::PopStyleVar(2);

for (auto& layer : m_LayerStack)
layer->OnUIRender();
{
ImGui::PushStyleColor(ImGuiCol_Border, IM_COL32(50, 50, 50, 255));
// Draw window border if the window is not maximized
if (!isMaximized)
UI::RenderWindowOuterBorders(ImGui::GetCurrentWindow());

ImGui::End();
ImGui::PopStyleColor(); // ImGuiCol_Border
}

// Rendering
ImGui::Render();
ImDrawData* main_draw_data = ImGui::GetDrawData();
const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f);
wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w;
wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w;
wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w;
wd->ClearValue.color.float32[3] = clear_color.w;
if (!main_is_minimized)
FrameRender(wd, main_draw_data);

// Update and Render additional Platform Windows
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
if (m_Specification.CustomTitlebar)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
float titleBarHeight;
UI_DrawTitlebar(titleBarHeight);
ImGui::SetCursorPosY(titleBarHeight);
}

// Present Main Platform Window
if (!main_is_minimized)
FramePresent(wd);
else
std::this_thread::sleep_for(std::chrono::milliseconds(5));
// Dockspace
ImGuiStyle& style = ImGui::GetStyle();
float minWinSizeX = style.WindowMinSize.x;
style.WindowMinSize.x = 370.0f;
ImGui::DockSpace(ImGui::GetID("MyDockspace"));
style.WindowMinSize.x = minWinSizeX;

if (!m_Specification.CustomTitlebar)
UI_DrawMenubar();

for (auto& layer : m_LayerStack)
layer->OnUIRender();

float time = GetTime();
m_FrameTime = time - m_LastFrameTime;
m_TimeStep = glm::min<float>(m_FrameTime, 0.0333f);
m_LastFrameTime = time;
ImGui::End();
}

// Rendering
ImGui::Render();
ImDrawData* main_draw_data = ImGui::GetDrawData();
m_MainMinimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f);
wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w;
wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w;
wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w;
wd->ClearValue.color.float32[3] = clear_color.w;
if (!m_MainMinimized)
FrameRender(wd, main_draw_data);

// Update and Render additional Platform Windows
const ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}

// Present Main Platform Window
if (!m_MainMinimized)
FramePresent(wd);

float time = GetTime();
m_FrameTime = time - m_LastFrameTime;
m_TimeStep = glm::min<float>(m_FrameTime, 0.0333f);
m_LastFrameTime = time;
}


void Application::Close()
{
m_Running = false;
Expand Down
2 changes: 2 additions & 0 deletions Walnut/Platform/GUI/Walnut/ApplicationGUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace Walnut {
static Application& Get();

void Run();
void Frame();
void SetMenubarCallback(const std::function<void()>& menubarCallback) { m_MenubarCallback = menubarCallback; }

template<typename T>
Expand Down Expand Up @@ -95,6 +96,7 @@ namespace Walnut {
ApplicationSpecification m_Specification;
GLFWwindow* m_WindowHandle = nullptr;
bool m_Running = false;
bool m_MainMinimized = false;

float m_TimeStep = 0.0f;
float m_FrameTime = 0.0f;
Expand Down