ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
59.6k stars 10.16k forks source link

UI elements cannot be interacted with or docked with each other (Win32 + Vulkan) #7656

Closed visualFatigue closed 3 months ago

visualFatigue commented 3 months ago

Version/Branch of Dear ImGui:

Version 1.79 (1.90.4-docking is the same)

Back-ends:

imgui_impl_win32.cpp + imgui_impl_vulkan.cpp

Compiler, OS:

Windows10 + MSVC 2022

Full config/build information:

The following info is from the example_win32_directx12

Dear ImGui 1.90.4 (19040)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1939
define: _MSVC_LANG=201402
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_win32
io.BackendRendererName: imgui_impl_dx12
io.ConfigFlags: 0x00000443
 NavEnableKeyboard
 NavEnableGamepad
 DockingEnable
 ViewportsEnable
io.ConfigViewportsNoDecoration
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,64
io.DisplaySize: 1264.00,761.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

Hi all, The problem that all UI elements (including the main window) became uninteractive (like the drag drop operation, but the interactions within these UI are normal) occurred when I changed the platform binding of my engine from the GLFW to the Win32 (the GLFW version is all normal). I'll try to post all the relevant code (maybe I can provide source code if someone insterested).

Screenshots/Video:

test

Minimal, Complete and Verifiable Example code:

// main

extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

bool g_ApplicationRunning = true;

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam))
        return true;

    switch (uMsg)
    {
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int main(int, char**)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_CLASSDC;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = TEXT("myWindowClass");
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        return 0;
    }

    HWND hwnd;
    hwnd = CreateWindow(
        wc.lpszClassName,
        TEXT("Editor Sample"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 1600, 900,
        NULL, NULL, wc.hInstance, NULL);

    if (hwnd == NULL)
    {
        return 0;
    }

    ShowWindow(hwnd, SW_SHOWDEFAULT);
    UpdateWindow(hwnd);

    while (g_ApplicationRunning)
    {
        // 1. initizalize
        // 2. render loop
        // 3. release
    }

    DestroyWindow(hwnd);
    UnregisterClass(wc.lpszClassName, wc.hInstance);
}

// from https://github.com/ocornut/imgui/issues/7377
static int ImGui_ImplWin32_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface)
    {
        VkSurfaceKHR surface;
        HWND windowHandle = static_cast<HWND>(viewport->PlatformHandleRaw);
        VkWin32SurfaceCreateInfoKHR surfaceInfo{ VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
        surfaceInfo.hwnd = windowHandle;
        surfaceInfo.hinstance = GetModuleHandle(NULL);

        VK_CHECK_RESULT(vkCreateWin32SurfaceKHR((VkInstance)vk_instance, &surfaceInfo, nullptr, &surface));

        *out_vk_surface = (ImU64)surface;
        return 0;
    }

// initizalize ImGui

    {
        IMGUI_CHECKVERSION();
        ImGui::CreateContext();
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
        io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
        io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;

        // https://github.com/ocornut/imgui/discussions/5871?sort=new
        ImGui::GetPlatformIO().Platform_CreateVkSurface = ImGui_ImplWin32_CreateVkSurface;
        ImGui_ImplWin32_Init(app.GetWindow().GetNativeWindow());
        ImGui_ImplVulkan_Init(&init_info, swapChain.GetRenderPass());
    }

// render ImGui

    {
        ImGui_ImplVulkan_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();
        // ...
        ImGui::Render();
        // ...
        ImGui_ImplVulkan_RenderDrawData(main_draw_data, s_ImGuiCommandBuffers[commandBufferIndex]);
        // ...
        if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
        {
               ImGui::UpdatePlatformWindows();
               ImGui::RenderPlatformWindowsDefault();
        }
    }

// release ImGui

    {
        ImGui_ImplVulkan_Shutdown();
        ImGui_ImplWin32_Shutdown();
        ImGui::DestroyContext();
    }
ocornut commented 3 months ago

You are not posting enough details about the code. Did you omit the Win32 event loop from your code? (PeekMessage(), TranslateMessage(), DispatchMessage(), as showcased in our examples).

GamingMinds-DanielC commented 3 months ago
    while (g_ApplicationRunning)
    {
        // 1. initizalize
        // 2. render loop
        // 3. release
    }

This looks very sketchy. Initialization and shutdown should be before and after the update/render loop respectively, not within the loop.

visualFatigue commented 3 months ago

Thanks for your timely reply , I omitted some codes that seem irrelevant to the topic, here are their complete contents while (g_ApplicationRunning) { XXX::InitializeCore(); XXX::Application* app = Respawn::CreateApplication(hwnd); // initialize app->Run(); // process loop delete app; // release XXX::ShutdownCore(); }

and I execute the Win32 event loop inside the app loop app->Run(); This is what it looks like inside ` void Application::Run() { OnInit(); while (m_Running) { m_Window->ProcessEvents(); // the event loop is here

        if (!m_Minimized)
        {
            // render
        }

    }
    OnShutdown();
}`

and the ProcessEvents() is void WindowsWindow::ProcessEvents() { MSG msg = { 0 }; while (PeekMessage(&msg, m_Window, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } }

by the way I replace the outside WndProc function in the initialize stage using this way SetWindowLongPtr(m_Window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(&m_Data)); SetWindowLongPtr(m_Window, GWLP_WNDPROC, (LONG_PTR)WindowProc);

and this is the actual WindowProc function ` static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { auto& data = ((WindowsWindow::WindowData)GetWindowLongPtr(hwnd, GWLP_USERDATA));

    switch (uMsg)
    {
    case WM_CLOSE:
        OnWindowClose(data);
        break;
        //case WM_CREATE:
        //  OnWindowCreate(hwnd);
        //  break;
    case WM_SIZE:
        OnWindowResize(data, LOWORD(lParam), HIWORD(lParam));
        break;
    case WM_MOUSEMOVE:
        OnMouseMove(data, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
        break;
    case WM_LBUTTONDOWN:
        OnLButtonDown(data, wParam);
        break;
    case WM_LBUTTONUP:
        OnLButtonUp(data, wParam);
        break;
    case WM_MOUSEWHEEL:
        OnMouseWheel(data, GET_WHEEL_DELTA_WPARAM(wParam));
        break;
    case WM_KEYDOWN:
        OnKeyDown(data, wParam);
        break;
    case WM_KEYUP:
        OnKeyUp(data, wParam);
        break;
    case WM_CHAR:
        OnChar(data, wParam);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}`

The purpose of all these designs is to mimic how the GLFW platform binding operates, to make platform binding replacement as transparent as possible.

GamingMinds-DanielC commented 3 months ago

by the way I replace the outside WndProc function in the initialize stage using this way

There is your problem. The function you replaced was the one that delegated messages to ImGui_ImplWin32_WndProcHandler(), which is required for working interactions. Your replacement doesn't do that, but it needs to.

visualFatigue commented 3 months ago

I find the problem! I forget call the if (ImGui_ImplWin32_WndProcHandler(hwnd, uMsg, wParam, lParam)) return true; in the internal WindowProc(). After that, everything is normal. But when I move any element out of the main window, it seems like all UI elements stop responding. test

ocornut commented 3 months ago

Glad it fixed it for you.

But.... you have posted a "complete and verifiable example code" that used a different proc handler that called ImGui_ImplWin32_WndProcHandler(). Your template has other contradictions too. I am tired of people making lazy/erroneous support request and stealing our time this way, it is not very respectful of our time.

visualFatigue commented 3 months ago

by the way I replace the outside WndProc function in the initialize stage using this way

There is your problem. The function you replaced was the one that delegated messages to ImGui_ImplWin32_WndProcHandler(), which is required for working interactions. Your replacement doesn't do that, but it needs to.

yeah I discovered the problem almost immediately when I posted the reply

visualFatigue commented 3 months ago

Glad it fixed it for you.

But.... you have posted a "complete and verifiable example code" that used a different proc handler that called ImGui_ImplWin32_WndProcHandler(). Your template has other contradictions too. I am tired of people making lazy/erroneous support request and stealing our time this way, it is not very respectful of our time.

So sorry, this is my bad