imgui
imgui copied to clipboard
Window pops to background when the main App window is moved (Z-order problem)
Version/Branch of Dear ImGui: Version: 1.89 WIP (18832) Branch: docking imgui_impl_win32 imgui_impl_dx11
Dear ImGui 1.89 WIP (18832)
--------------------------------
sizeof(size_t): 4, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _MSC_VER=1916
define: _MSVC_LANG=201402
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
Back-end/Renderer/Compiler/OS Back-ends: imgui_impl_win32.cpp + imgui_impl_dx11.cpp Compiler: Visual Studio 2017 Operating System: MS Windows 10 Pro
My Issue/Question:
A window (i.e. ImGui::BeginWindow) behaves nicely until it is add as a child to the main App window. When the main App window is moved (or resized), the child window moves along with it (as expected), but then it pops to the background below all the main window content in the App window and is no longer clickable. The main App window is built with a ImGui::BeginWindow / ImGui::EndWindow sequence that is set up to fill the app window. I tried using the ImGuiWindowFlags_NoBringToFrontOnFocus on the main window to keep it in the background as mentioned in another issue, but that didn't help.
Screenshots/Video
https://user-images.githubusercontent.com/6425918/199611494-4f9c3189-1c9f-4dd6-900f-a04d6d3b7eab.mp4
Standalone, minimal, complete and verifiable example: The complete example below shows the behavior in the video above. (Sorry if it's not minimal enough, but I figured a complete working example would be easier to deal with.) The larger window sticks out of the main window and is moveable. The smaller window inside the main window seems to be behind the full-window ImGui app window so it is unclickable. If you resize the main window so the small window sticks out, it becomes clickable.
If you move either window back inside the main window, they are still on top and clickable. If you just move the main app window, those smaller windows move together with it and are sometimes still clickable. But then if you resize the main window, the windows inside seem to be pushed behind the full-window ImGui app window and become un-clickable. My hope is that this wouldn't happen, or that I could explicitly control the Z-ordering of windows. I understand that is being considered from some of the other github issues I've read.
Thanks for your help!!
// Dear ImGui: standalone example application for DirectX 11
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"
#include <d3d11.h>
#include <tchar.h>
#include <string>
#include <vector>
// Data
static ID3D11Device* g_pd3dDevice = NULL;
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
static IDXGISwapChain* g_pSwapChain = NULL;
static ID3D11RenderTargetView* g_mainRenderTargetView = NULL;
// Forward declarations of helper functions
bool CreateDeviceD3D(HWND hWnd);
void CleanupDeviceD3D();
void CreateRenderTarget();
void CleanupRenderTarget();
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
float BaseImGuiFontSize = 14.0f;
float TargetImGuiFontSize = 14.0f;
bool sShowVisualizerTab = false;
std::vector<std::string> recentFiles;
bool fileLoaded = true;
bool canSave = true;
// Main code
int main(int, char**)
{
// Create application window
//ImGui_ImplWin32_EnableDpiAwareness();
WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"ImGui Example", NULL };
::RegisterClassExW(&wc);
HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, 500, 800, NULL, NULL, wc.hInstance, NULL);
// Initialize Direct3D
if (!CreateDeviceD3D(hwnd))
{
CleanupDeviceD3D();
::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 1;
}
// Show the window
::ShowWindow(hwnd, SW_SHOWDEFAULT);
::UpdateWindow(hwnd);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
#define MY_IMGUI_DOCKING 1
#define MY_IMGUI_VIEWPORTS 1
#ifdef MY_IMGUI_DOCKING
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigDockingWithShift = true;
io.ConfigDockingTransparentPayload = true;
#endif
#ifdef MY_IMGUI_VIEWPORTS
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows, ability to drag a window outside the app window
io.ConfigViewportsNoDecoration = true; // don't add Windows title bars on top of imgui title bars when the windows are outside the app window
#endif
io.ConfigWindowsResizeFromEdges = true; // allow drag on edges to resize windows
io.ConfigWindowsMoveFromTitleBarOnly = true; // no dragging from the middle of the window to move the window
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ImGuiStyle& style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
//style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
// Setup Platform/Renderer backends
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
// Load Fonts
// Our state
bool show_demo_window = true;
bool show_another_window = true;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
ImVec2 sViewPortPos = { 0,0 };
ImVec2 sViewPortSize = { 1000,1000 };
// Main loop
bool done = false;
while (!done)
{
// Poll and handle messages (inputs, window resize, etc.)
// See the WndProc() function below for our to dispatch events to the Win32 backend.
MSG msg;
while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
if (msg.message == WM_QUIT)
done = true;
}
if (done)
break;
// Start the Dear ImGui frame
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// Note that sViewPortPos and sViewPortSize are set below after ImGui::Render() so that they
// can query the display size to make the main window the full size of the main App window.
ImGui::SetNextWindowPos(sViewPortPos, ImGuiCond_Always);
ImGui::SetNextWindowSize(sViewPortSize, ImGuiCond_Always);
//Create the main window, which all subsequent windows get drawn inside
ImGui::Begin("MainWindow", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::BeginMenuBar();
if (ImGui::BeginMenu("File")) {
// TODO: turn off disabling load if a file is already loaded - by clearing out the state before loading a new file
if (ImGui::MenuItem("Load .json...", nullptr, false /* selected */, !fileLoaded /* enabled */)) {
}
if (ImGui::MenuItem("Save .json", "CTRL+S", false /* selected */, canSave /* enabled */)) {
}
if (ImGui::MenuItem("Save .json As...", "", false /* selected */, canSave /* enabled */)) {
}
if (ImGui::BeginMenu("Load Recent...", !fileLoaded && !recentFiles.empty()/* enabled */)) {
static int recentFileIndex = -1;
for (int i = 0; i < static_cast<int>(recentFiles.size()); ++i) {
if (ImGui::MenuItem(recentFiles[i].c_str(), nullptr, false /* selected */, !fileLoaded /* enabled */)) {
}
}
ImGui::EndMenu();
}
if (ImGui::MenuItem("Save All...", "SHIFT+CTRL+S", false /* selected */, canSave /* enabled */)) {
}
if (ImGui::MenuItem("Preferences...")) {
}
if (ImGui::MenuItem("About...")) {
}
if (ImGui::MenuItem("Quit", "CTRL+Q")) {
}
ImGui::EndMenu(); // only call EndMenu() if BeginMenu() returns true!
}
if (ImGui::BeginMenu("Edit")) {
if (ImGui::MenuItem("Undo", "CTRL+Z", false /* selected */, true /* enabled */)) {
}
if (ImGui::MenuItem("Redo", "CTRL+Y", false /* selected */, true /* enabled */)) {
}
if (ImGui::MenuItem("Clear All Completed", nullptr, false /* selected */, true /* enabled */)) {
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Clear all completed checkboxes:\nThis action is not undoable.");
}
ImGui::EndMenu(); // only call EndMenu() if BeginMenu() returns true!
}
if (ImGui::Button("Save All")) {
}
if (ImGui::Button("Update")) {
}
int id = 2398467;
// Up and Down arrows to set the font size of the whole UI
ImGui::SameLine();
//_PushImGuiFont(3);
{
ImGui::PushID(id++);
if (ImGui::Button("-"/*ICON_FA_ARROW_ALT_CIRCLE_DOWN*/)) { // Decrease font size by 1 unit
//_ShrinkImGuiFonts();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Shrink Fonts");
}
ImGui::PopID();
ImGui::SameLine();
ImGui::PushID(id++);
if (ImGui::Button("+" /*ICON_FA_ARROW_ALT_CIRCLE_UP*/)) { // Increase font size by 1 unit
//_GrowImGuiFonts();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Grow Fonts");
}
ImGui::PopID();
}
//ImGui::PopFont();
if (ImGui::Button("Back To Vehicle Type Selection"))
{
}
ImGui::SameLine();
if (ImGui::Button(sShowVisualizerTab ? "Hide Visualizer" : "Show Visualizer"))
{
sShowVisualizerTab = !sShowVisualizerTab;
}
ImGui::SameLine();
ImGui::Text("Track ID:");
ImGui::SameLine();
ImGui::SetNextItemWidth(20 * TargetImGuiFontSize);
static char trackIdBuf[200];
ImGui::InputText("##trackId", trackIdBuf, 200);
ImGui::SameLine();
ImGui::Text("Track Type:");
ImGui::SameLine();
ImGui::SetNextItemWidth(9 * TargetImGuiFontSize);
static char trackTypeBuf[200];
ImGui::InputText("##trackType", trackTypeBuf, 200);
ImGui::EndMenuBar();
ImGui::End();
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
{
static float f = 0.0f;
static int counter = 0;
ImVec2 winPos1 = { 600, 400 };
ImGui::SetNextWindowPos(winPos1, ImGuiCond_Appearing);
ImGui::Begin("Bigger Window"); // Create a window called "Hello, world!" and append into it.
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
ImGui::Checkbox("Another Window", &show_another_window);
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
}
// 3. Show another simple window.
if (show_another_window)
{
ImVec2 winPos2 = { 380, 700 };
ImGui::SetNextWindowPos(winPos2, ImGuiCond_Appearing);
ImGui::Begin("Smaller Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
ImGui::Text("Hello from another window!");
if (ImGui::Button("Close Me"))
show_another_window = false;
ImGui::End();
}
// Rendering
ImGui::Render();
// Set the App's main window size by querying the display area
// This makes the main window fill the whole main window of the App
if (ImGui::GetDrawData()) {
sViewPortPos = ImGui::GetDrawData()->DisplayPos;
sViewPortSize = ImGui::GetDrawData()->DisplaySize;
}
const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL);
g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
// Update and Render additional Platform Windows
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
g_pSwapChain->Present(1, 0); // Present with vsync
//g_pSwapChain->Present(0, 0); // Present without vsync
}
// Cleanup
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
CleanupDeviceD3D();
::DestroyWindow(hwnd);
::UnregisterClassW(wc.lpszClassName, wc.hInstance);
return 0;
}
// Helper functions
bool CreateDeviceD3D(HWND hWnd)
{
// Setup swap chain
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2;
sd.BufferDesc.Width = 0;
sd.BufferDesc.Height = 0;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
UINT createDeviceFlags = 0;
//createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
D3D_FEATURE_LEVEL featureLevel;
const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
if (D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext) != S_OK)
return false;
CreateRenderTarget();
return true;
}
void CleanupDeviceD3D()
{
CleanupRenderTarget();
if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = NULL; }
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; }
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
}
void CreateRenderTarget()
{
ID3D11Texture2D* pBackBuffer;
g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_mainRenderTargetView);
pBackBuffer->Release();
}
void CleanupRenderTarget()
{
if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = NULL; }
}
#ifndef WM_DPICHANGED
#define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers
#endif
// Forward declare message handler from imgui_impl_win32.cpp
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Win32 message handler
// 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, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true;
switch (msg)
{
case WM_SIZE:
if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED)
{
CleanupRenderTarget();
g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
CreateRenderTarget();
}
return 0;
case WM_SYSCOMMAND:
if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
return 0;
break;
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
case WM_DPICHANGED:
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports)
{
//const int dpi = HIWORD(wParam);
//printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f);
const RECT* suggested_rect = (RECT*)lParam;
::SetWindowPos(hWnd, NULL, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
}
return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
Any ideas about this one? Is there something wrong with the way I wrote up the issue?
Is there something wrong with the way I wrote up the issue?
A large wall of code puts the burden on us to read through it all and try to discern what might be going wrong. (Or to guess and make our own minimal repro.)
(Sorry if it's not minimal enough, but I figured a complete working example would be easier to deal with.)
The idea from the minimal reproducible example is that you can give us something we can copy+paste into one of the official Dear ImGui example applications to reproduce the bug.
It also gives us some confidence you've spent the effort of trying to whittle things down to the basics as part of your own effort to debug things.
For example, your code has a bunch of stuff relating to your main menu bar boilerplate, Direct3D setup, and Font Awesome. All of which are very unlikely to be related to the problem at hand and just serve as noise.