From 34b3d32d1aad395d92f4cd1bd8e8fa0f0ceba5d7 Mon Sep 17 00:00:00 2001 From: Sergey Nenakhov Date: Fri, 18 Mar 2022 10:57:26 +0100 Subject: [PATCH 1/2] - fixed plot animations - added api function (ImGui::SetNextRefresh) - fixed windows not redrawing when closing via close button --- backends/imgui_impl_dx12.cpp | 18 ++- backends/imgui_impl_dx12.h | 1 + backends/imgui_impl_win32.cpp | 137 +++++++++++++---- backends/imgui_impl_win32.h | 2 +- examples/example_win32_directx12/main.cpp | 80 ++++++++-- imgui.cpp | 17 +++ imgui.h | 172 ++++++++++++---------- imgui_demo.cpp | 26 ++-- imgui_widgets.cpp | 25 +++- 9 files changed, 344 insertions(+), 134 deletions(-) diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 6b9d29ecc4e0..3c2d16b3fde9 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -284,6 +284,11 @@ static void ImGui_ImplDX12_CreateFontsTexture() // Build texture atlas ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + + // clear prior textures if any + SafeRelease(bd->pFontTextureResource); + + unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); @@ -420,7 +425,6 @@ static void ImGui_ImplDX12_CreateFontsTexture() srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, bd->hFontSrvCpuDescHandle); - SafeRelease(bd->pFontTextureResource); bd->pFontTextureResource = pTexture; } @@ -664,11 +668,17 @@ bool ImGui_ImplDX12_CreateDeviceObjects() if (result_pipeline_state != S_OK) return false; - ImGui_ImplDX12_CreateFontsTexture(); - return true; } +void ImGui_ImplDX12_InvalidateFontTexture() +{ + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + if (!bd || !bd->pd3dDevice) + return; + SafeRelease(bd->pFontTextureResource); +} + void ImGui_ImplDX12_InvalidateDeviceObjects() { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); @@ -743,4 +753,6 @@ void ImGui_ImplDX12_NewFrame() if (!bd->pPipelineState) ImGui_ImplDX12_CreateDeviceObjects(); + if (!bd->pFontTextureResource) // texture can be released in case of dpi change + ImGui_ImplDX12_CreateFontsTexture(); } diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index 6548f6f566f2..eaca071f9870 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -34,5 +34,6 @@ IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame(); IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list); // Use if you want to reset your rendering device without losing Dear ImGui state. +IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateFontTexture(); IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 8977a1f6c8cd..570e468697cc 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -78,15 +78,16 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); struct ImGui_ImplWin32_Data { + INT64 Time; + INT64 TicksPerSecond; HWND hWnd; HWND MouseHwnd; bool MouseTracked; - int MouseButtonsDown; - INT64 Time; - INT64 TicksPerSecond; - ImGuiMouseCursor LastMouseCursor; bool HasGamepad; bool WantUpdateHasGamepad; + int MouseButtonsDown; + int MouseX, MouseY; // against spurious WM_MOUSEMOVE events + ImGuiMouseCursor LastMouseCursor; #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD HMODULE XInputDLL; @@ -129,7 +130,10 @@ bool ImGui_ImplWin32_Init(void* hwnd) bd->WantUpdateHasGamepad = true; bd->TicksPerSecond = perf_frequency; bd->Time = perf_counter; + //bd->LastMouseCursor = ImGuiMouseCursor_Arrow; //windows default is the arrow, no need to reset it bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + bd->MouseX = -1; + bd->MouseY = -1; // Set platform dependent data in viewport ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd; @@ -180,8 +184,15 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) return false; - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + + ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); + if (bd->LastMouseCursor == mouse_cursor) + return false; + + bd->LastMouseCursor = mouse_cursor; + + if (mouse_cursor == ImGuiMouseCursor_None /*|| io.MouseDrawCursor*/) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor ::SetCursor(NULL); @@ -190,7 +201,7 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() { // Show OS mouse cursor LPTSTR win32_cursor = IDC_ARROW; - switch (imgui_cursor) + switch (mouse_cursor) { case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; @@ -328,20 +339,59 @@ static void ImGui_ImplWin32_UpdateGamepads() #endif // #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD } -void ImGui_ImplWin32_NewFrame() +bool ImGui_ImplWin32_NewFrame(bool poll_only) { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); IM_ASSERT(bd != NULL && "Did you call ImGui_ImplWin32_Init()?"); + INT64 current_time; + + + + + for (;;) + { + ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); + + double next_refresh = !poll_only ? io.NextRefresh : 0.0; + + double cur_delta = double(current_time - bd->Time) / bd->TicksPerSecond; + if (cur_delta <= next_refresh) + { + double ms_to_wait_double = (next_refresh - cur_delta) * 1000.0f; + unsigned int ms_to_wait = ms_to_wait_double >= MAXDWORD ? INFINITE : unsigned int(ms_to_wait_double); + if (ms_to_wait) + MsgWaitForMultipleObjectsEx(0, nullptr, ms_to_wait, QS_ALLEVENTS, 0); + } + + + MSG msg; + while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + { + if (msg.message != WM_QUIT) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + continue; + } + return false; + } + + + if (cur_delta <= next_refresh) + continue; + + break; + } + + // Setup display size (every frame to accommodate for window resizing) RECT rect = { 0, 0, 0, 0 }; ::GetClientRect(bd->hWnd, &rect); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); // Setup time step - INT64 current_time = 0; - ::QueryPerformanceCounter((LARGE_INTEGER*)¤t_time); io.DeltaTime = (float)(current_time - bd->Time) / bd->TicksPerSecond; bd->Time = current_time; @@ -352,15 +402,12 @@ void ImGui_ImplWin32_NewFrame() ImGui_ImplWin32_ProcessKeyEventsWorkarounds(); // Update OS mouse cursor with the cursor requested by imgui - ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (bd->LastMouseCursor != mouse_cursor) - { - bd->LastMouseCursor = mouse_cursor; - ImGui_ImplWin32_UpdateMouseCursor(); - } - + ImGui_ImplWin32_UpdateMouseCursor(); + // Update game controllers (if enabled and available) ImGui_ImplWin32_UpdateGamepads(); + + return true; } // There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255) @@ -504,13 +551,18 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (ImGui::GetCurrentContext() == NULL) return 0; + const char* key_refresh_reason; ImGuiIO& io = ImGui::GetIO(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); switch (msg) { case WM_MOUSEMOVE: + // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events + if (!bd->MouseHwnd) // mouse entered client area + bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + bd->MouseHwnd = hwnd; if (!bd->MouseTracked) { @@ -518,19 +570,38 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA ::TrackMouseEvent(&tme); bd->MouseTracked = true; } - io.AddMousePosEvent((float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam)); + + { + int m_x = GET_X_LPARAM(lParam), m_y = GET_Y_LPARAM(lParam); + if (bd->MouseX != m_x || bd->MouseY != m_y) // spurious WM_MOUSEMOVE events are a real thing. don't act on them + { + bd->MouseX = m_x, bd->MouseY = m_y; + + io.SetNextRefresh(0, "mouse move"); //return 0; + + io.AddMousePosEvent((float)m_x, (float)m_y); + } + } break; case WM_MOUSELEAVE: if (bd->MouseHwnd == hwnd) bd->MouseHwnd = NULL; bd->MouseTracked = false; + //bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + bd->MouseX = -1, bd->MouseY = -1; io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); break; - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: - case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: + case WM_LBUTTONDOWN: key_refresh_reason = "mouse lbuttonup down"; goto md; + case WM_LBUTTONDBLCLK: key_refresh_reason = "mouse lbuttonup dblclk"; goto md; + case WM_RBUTTONDOWN: key_refresh_reason = "mouse rbuttonup down"; goto md; + case WM_RBUTTONDBLCLK: key_refresh_reason = "mouse rbuttonup dblclk"; goto md; + case WM_MBUTTONDOWN: key_refresh_reason = "mouse mbuttonup donw"; goto md; + case WM_MBUTTONDBLCLK: key_refresh_reason = "mouse mbuttonup dblclk"; goto md; + case WM_XBUTTONDOWN: key_refresh_reason = "mouse xbuttonup down"; goto md; + case WM_XBUTTONDBLCLK: key_refresh_reason = "mouse xbuttonup dblclk"; + md: { + io.SetNextRefresh(0, key_refresh_reason); int button = 0; if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } @@ -542,11 +613,13 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA io.AddMouseButtonEvent(button, true); return 0; } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - case WM_XBUTTONUP: + case WM_LBUTTONUP: key_refresh_reason = "mouse lbutton up"; goto mu; + case WM_RBUTTONUP:key_refresh_reason = "mouse rbutton up"; goto mu; + case WM_MBUTTONUP:key_refresh_reason = "mouse mbutton up"; goto mu; + case WM_XBUTTONUP:key_refresh_reason = "mouse xbutton up"; + mu: { + io.SetNextRefresh(0, key_refresh_reason); int button = 0; if (msg == WM_LBUTTONUP) { button = 0; } if (msg == WM_RBUTTONUP) { button = 1; } @@ -559,19 +632,27 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA return 0; } case WM_MOUSEWHEEL: + io.SetNextRefresh(0, "wheel up"); io.AddMouseWheelEvent(0.0f, (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); return 0; case WM_MOUSEHWHEEL: + io.SetNextRefresh(0, "wheel down"); io.AddMouseWheelEvent((float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); return 0; case WM_KEYDOWN: + key_refresh_reason = "key down"; goto l; case WM_KEYUP: + key_refresh_reason = "key up"; goto l; case WM_SYSKEYDOWN: + key_refresh_reason = "syskey down"; goto l; case WM_SYSKEYUP: - { - const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); + key_refresh_reason = "key up"; +l: + { + const bool is_key_down = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN); if (wParam < 256) { + io.SetNextRefresh(0, key_refresh_reason); // Submit modifiers ImGui_ImplWin32_UpdateKeyModifiers(); diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 082461dd5782..e893e7998476 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -17,7 +17,7 @@ IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); +IMGUI_IMPL_API bool ImGui_ImplWin32_NewFrame(bool poll = false); // Win32 message handler your application need to call. // - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index 6f6e680b0b0f..f4a431205207 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #ifdef _DEBUG #define DX12_ENABLE_DEBUG_LAYER @@ -29,11 +32,11 @@ struct FrameContext }; // Data -static int const NUM_FRAMES_IN_FLIGHT = 3; +static int const NUM_FRAMES_IN_FLIGHT = 1; static FrameContext g_frameContext[NUM_FRAMES_IN_FLIGHT] = {}; static UINT g_frameIndex = 0; -static int const NUM_BACK_BUFFERS = 3; +static int const NUM_BACK_BUFFERS = 2; static ID3D12Device* g_pd3dDevice = NULL; static ID3D12DescriptorHeap* g_pd3dRtvDescHeap = NULL; static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = NULL; @@ -55,12 +58,13 @@ void CleanupRenderTarget(); void WaitForLastSubmittedFrame(); FrameContext* WaitForNextFrameResources(); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +void HandleDpiChange(float dpi_scale); // Main code int main(int, char**) { // Create application window - //ImGui_ImplWin32_EnableDpiAwareness(); + //ImGui_ImplWin32_EnableDpiAwareness(); // COMMENTED OUT SINCE WINDOWS RECOMMENDATION IS TO USE MANIFEST AND NOT USE FUNCTIONS TO DYNAMIC ENABLE DPI AWARENESS WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; ::RegisterClassEx(&wc); HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX12 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL); @@ -84,6 +88,10 @@ int main(int, char**) //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + ImGui::GetIO().SetNextRefresh(0, "first frame"); + + HandleDpiChange(float(GetDpiForWindow(hwnd) / 96.0)); + // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); @@ -114,6 +122,7 @@ int main(int, char**) bool show_demo_window = true; bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + int fame_number = 0; // Main loop bool done = false; @@ -121,6 +130,7 @@ int main(int, char**) { // Poll and handle messages (inputs, window resize, etc.) // See the WndProc() function below for our to dispatch events to the Win32 backend. +#if 0 MSG msg; while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { @@ -129,12 +139,18 @@ int main(int, char**) if (msg.message == WM_QUIT) done = true; } +#endif + if (done) break; // Start the Dear ImGui frame + if (!ImGui_ImplWin32_NewFrame()) + break; + + printf("rendeing frame %i, reason: %s (%5.2fs)... ", fame_number++, io.GetNextRefreshReason(), io.NextRefresh >= FLT_MAX ? 99.99f : io.NextRefresh); ImGui_ImplDX12_NewFrame(); - ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). @@ -149,7 +165,8 @@ int main(int, char**) ImGui::Begin("Hello, world!"); // 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 + if (ImGui::Checkbox("Demo Window", &show_demo_window)) // Edit bools storing our window open/close state + io.SetNextRefresh(0, "Demo Window toggled"); 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 @@ -171,12 +188,20 @@ int main(int, char**) ImGui::Text("Hello from another window!"); if (ImGui::Button("Close Me")) show_another_window = false; + if (!show_another_window) + io.SetNextRefresh(0, "User window close"); ImGui::End(); } // Rendering ImGui::Render(); + if (io.NextRefresh >= FLT_MAX) + printf("\n"); + else + printf("\tnextrefresh: %s(in %5.2fs)\n", io.GetNextRefreshReason(), io.NextRefresh >= FLT_MAX ? 99.99f : io.NextRefresh); + + FrameContext* frameCtx = WaitForNextFrameResources(); UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex(); frameCtx->CommandAllocator->Reset(); @@ -249,12 +274,23 @@ bool CreateDeviceD3D(HWND hWnd) sd.Stereo = FALSE; } + UINT dxgiFactoryFlags = 0; + // [DEBUG] Enable debug interface #ifdef DX12_ENABLE_DEBUG_LAYER ID3D12Debug* pdx12Debug = NULL; if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&pdx12Debug)))) + { pdx12Debug->EnableDebugLayer(); + ID3D12Debug1* debug1; + if (SUCCEEDED(pdx12Debug->QueryInterface(IID_PPV_ARGS(&debug1)))) + { + debug1->SetEnableGPUBasedValidation(TRUE); + debug1->Release(); + } + } #endif + dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; // Create device D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; @@ -266,11 +302,13 @@ bool CreateDeviceD3D(HWND hWnd) if (pdx12Debug != NULL) { ID3D12InfoQueue* pInfoQueue = NULL; - g_pd3dDevice->QueryInterface(IID_PPV_ARGS(&pInfoQueue)); - pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); - pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); - pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true); - pInfoQueue->Release(); + if (SUCCEEDED(g_pd3dDevice->QueryInterface(IID_PPV_ARGS(&pInfoQueue)))) + { + pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); + pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); + pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, true); + pInfoQueue->Release(); + } pdx12Debug->Release(); } #endif @@ -329,7 +367,7 @@ bool CreateDeviceD3D(HWND hWnd) { IDXGIFactory4* dxgiFactory = NULL; IDXGISwapChain1* swapChain1 = NULL; - if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK) + if (CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&dxgiFactory)) != S_OK) return false; if (dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, NULL, NULL, &swapChain1) != S_OK) return false; @@ -337,7 +375,7 @@ bool CreateDeviceD3D(HWND hWnd) return false; swapChain1->Release(); dxgiFactory->Release(); - g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS); + g_pSwapChain->SetMaximumFrameLatency(/*NUM_BACK_BUFFERS*/1); g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject(); } @@ -428,6 +466,21 @@ FrameContext* WaitForNextFrameResources() return frameCtx; } +void HandleDpiChange(float dpi_scale) +{ + ImGui_ImplDX12_InvalidateFontTexture(); + + char dir[512]; + GetSystemDirectoryA(dir, _countof(dir)); + + ImGui::GetStyle().ScaleAllSizes(dpi_scale); + + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->Clear(); + io.Fonts->AddFontFromFileTTF((std::string(dir) + "\\..\\Fonts\\segoeui.ttf").c_str(), roundf(16 * dpi_scale)); + io.SetNextRefresh(0, "dpi changed"); +} + // 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); @@ -443,6 +496,9 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) switch (msg) { + case WM_DPICHANGED: + HandleDpiChange(float(HIWORD(wParam) / 96.0)); + return 0; case WM_SIZE: if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) { diff --git a/imgui.cpp b/imgui.cpp index 0949f4307983..921648a333c7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4449,6 +4449,9 @@ void ImGui::NewFrame() g.ItemFlagsStack.push_back(ImGuiItemFlags_None); g.GroupStack.resize(0); + // set 'no need for refresh later' initially until one of the widgets tells otherwise + g.IO.NextRefresh = FLT_MAX; g.IO.SetNextRefresh(FLT_MAX, ""); + // [DEBUG] Update debug features UpdateDebugToolItemPicker(); UpdateDebugToolStackQueries(); @@ -5878,12 +5881,18 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) if (has_collapse_button) if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) + { window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function + g.IO.SetNextRefresh(0, "window collapse toggled"); + } // Close button if (has_close_button) if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) + { *p_open = false; + g.IO.SetNextRefresh(0, "window close via closebutton"); + } window->DC.NavLayerCurrent = ImGuiNavLayer_Main; g.CurrentItemFlags = item_flags_backup; @@ -6162,13 +6171,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Hide new windows for one frame until they calculate their size if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) + { window->HiddenFramesCannotSkipItems = 1; + g.IO.SetNextRefresh(0, "HiddenFrames JustCreated");// don't delay rendering of new frame to get hidden frames working + } // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) { window->HiddenFramesCannotSkipItems = 1; + g.IO.SetNextRefresh(0, "HiddenFrames JustActivated"); // don't delay rendering of new frame to get hidden frames working if (flags & ImGuiWindowFlags_AlwaysAutoResize) { if (!window_size_x_set_by_api) @@ -11519,6 +11532,10 @@ void ImGui::LogButtons() LogToClipboard(); } +void ImGui::SetNextRefresh(float delay_in_seconds, const char* refresh_reason) +{ + GetIO().SetNextRefresh(delay_in_seconds, refresh_reason); +} //----------------------------------------------------------------------------- // [SECTION] SETTINGS diff --git a/imgui.h b/imgui.h index f92c70dc61b1..42cabfc50ac4 100644 --- a/imgui.h +++ b/imgui.h @@ -261,7 +261,7 @@ struct ImVec2 // ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type] struct ImVec4 { - float x, y, z, w; + float x, y, z, w; constexpr ImVec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) { } constexpr ImVec4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) { } #ifdef IM_VEC4_CLASS_EXTRA @@ -933,6 +933,8 @@ namespace ImGui IMGUI_API void* MemAlloc(size_t size); IMGUI_API void MemFree(void* ptr); + + IMGUI_API void SetNextRefresh(float delay_in_seconds, const char* refresh_reason); } // namespace ImGui //----------------------------------------------------------------------------- @@ -1397,26 +1399,26 @@ enum ImGuiKey_ ImGuiKey_KeypadEqual, // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION action - ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // -- - ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // -- - ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input - ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate - ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu - ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel - ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp - ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown - ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft - ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight - ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow - ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast - ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog] - ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog] - ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS) - ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS) - ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp - ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown - ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft - ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight + ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // -- + ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // -- + ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input + ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate + ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu + ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel + ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp + ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown + ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft + ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight + ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow + ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast + ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog] + ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog] + ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS) + ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS) + ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp + ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown + ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft + ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight ImGuiKey_GamepadRStickUp, // [Analog] ImGuiKey_GamepadRStickDown, // [Analog] ImGuiKey_GamepadRStickLeft, // [Analog] @@ -1433,7 +1435,7 @@ enum ImGuiKey_ ImGuiKey_ModCtrl, ImGuiKey_ModShift, ImGuiKey_ModAlt, ImGuiKey_ModSuper, // End of list - ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value + ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + a io.KeyMap[] array. // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) @@ -1456,11 +1458,11 @@ enum ImGuiKey_ // Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API. enum ImGuiKeyModFlags_ { - ImGuiKeyModFlags_None = 0, - ImGuiKeyModFlags_Ctrl = 1 << 0, - ImGuiKeyModFlags_Shift = 1 << 1, - ImGuiKeyModFlags_Alt = 1 << 2, - ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key + ImGuiKeyModFlags_None = 0, + ImGuiKeyModFlags_Ctrl = 1 << 0, + ImGuiKeyModFlags_Shift = 1 << 1, + ImGuiKeyModFlags_Alt = 1 << 2, + ImGuiKeyModFlags_Super = 1 << 3 // Cmd/Super/Windows key }; // Gamepad/Keyboard navigation @@ -1967,18 +1969,18 @@ struct ImGuiIO //------------------------------------------------------------------ // Input Functions - IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) + IMGUI_API void AddKeyEvent(ImGuiKey key, bool down); // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) IMGUI_API void AddKeyAnalogEvent(ImGuiKey key, bool down, float v); // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend. - IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) - IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change - IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update - IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) - IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input - IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate - IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string - - IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually - IMGUI_API void ClearInputKeys(); // [Internal] Release all keys + IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) + IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change + IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update + IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) + IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input + IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate + IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string + + IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually + IMGUI_API void ClearInputKeys(); // [Internal] Release all keys IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode. //------------------------------------------------------------------ @@ -1987,20 +1989,40 @@ struct ImGuiIO // generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!) //------------------------------------------------------------------ - bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). - bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). - bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. - bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! - bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. - bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. - int MetricsRenderVertices; // Vertices output during last call to Render() - int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 - int MetricsRenderWindows; // Number of visible windows - int MetricsActiveWindows; // Number of active windows - int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. - ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. + bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). + bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). + bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). + bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. + bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! + bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. + bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). + float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. + int MetricsRenderVertices; // Vertices output during last call to Render() + int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 + int MetricsRenderWindows; // Number of visible windows + int MetricsActiveWindows; // Number of active windows + int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. + ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. + float NextRefresh; // Optional: tells when should next render happen to see any change in results, 0 on init so that first draw goes through +#if 1 + char NextRefresh_dbg[64] = {}; + void SetNextRefresh(float delay_in_seconds, const char* dbg_reason) + { + if (delay_in_seconds <= NextRefresh) + { + NextRefresh = delay_in_seconds; +#ifdef _MSC_VER + strncpy_s(NextRefresh_dbg, dbg_reason, sizeof(NextRefresh_dbg) / sizeof(char) - 1); +#else + strncpy(NextRefresh_dbg, dbg_reason, sizeof(NextRefresh_dbg) / sizeof(char) - 1); +#endif + } + } + const char* GetNextRefreshReason() const { return NextRefresh_dbg; } +#else + void SetNextRefresh(float delay_in_seconds, const char* /*dbg_line*/) { if (delay_in_seconds <= NextRefresh) NextRefresh = delay_in_seconds; } + const char* GetNextRefreshReason() const { return ""; } +#endif // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent(). @@ -2027,30 +2049,30 @@ struct ImGuiIO float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). // Other state maintained from data above + IO function calls - ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() + ImGuiKeyModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this. bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. - ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) - ImVec2 MouseClickedPos[5]; // Position at time of clicking - double MouseClickedTime[5]; // Time of last click (used to figure out double-click) + ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) + ImVec2 MouseClickedPos[5]; // Position at time of clicking + double MouseClickedTime[5]; // Time of last click (used to figure out double-click) bool MouseClicked[5]; // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0) bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2) ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. - bool MouseReleased[5]; // Mouse button went from Down to !Down + bool MouseReleased[5]; // Mouse button went from Down to !Down bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. - bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. - float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) - float MouseDownDurationPrev[5]; // Previous time the mouse button has been down + bool MouseDownOwnedUnlessPopupClose[5]; //Track if button was clicked inside a dear imgui window. + float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) + float MouseDownDurationPrev[5]; // Previous time the mouse button has been down float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) float NavInputsDownDuration[ImGuiNavInput_COUNT]; float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; - float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. + float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. bool AppFocusLost; ImS8 BackendUsingLegacyKeyArrays; // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[] bool BackendUsingLegacyNavInputArray; // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly ImWchar16 InputQueueSurrogate; // For AddInputCharacterUTF16() - ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. + ImVector InputQueueCharacters; // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper. IMGUI_API ImGuiIO(); }; @@ -2306,8 +2328,8 @@ struct ImGuiListClipper IMGUI_API ImGuiListClipper(); IMGUI_API ~ImGuiListClipper(); IMGUI_API void Begin(int items_count, float items_height = -1.0f); - IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. + IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. // Call ForceDisplayRangeByIndices() before first call to Step() if you need a range of items to be displayed regardless of visibility. IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. @@ -2342,7 +2364,7 @@ struct ImGuiListClipper // **None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. Explicitly cast to ImU32 or ImVec4 if needed. struct ImColor { - ImVec4 Value; + ImVec4 Value; constexpr ImColor() { } constexpr ImColor(float r, float g, float b, float a = 1.0f) : Value(r, g, b, a) { } @@ -2990,16 +3012,16 @@ namespace ImGui //static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } // OBSOLETED in 1.72 (from July 2019) //static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.71 (from June 2019) //static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } // OBSOLETED in 1.70 (from May 2019) - //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) - //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) - //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) - //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) - //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) - //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) + //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) + //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) + //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) + //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) + //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) } // OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5f9742d1067f..9f1da94cf2b2 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -527,17 +527,17 @@ void ImGui::ShowDemoWindow(bool* p_open) { if (ImGui::BeginTable("split", 3)) { - ImGui::TableNextColumn(); ImGui::Checkbox("No titlebar", &no_titlebar); - ImGui::TableNextColumn(); ImGui::Checkbox("No scrollbar", &no_scrollbar); - ImGui::TableNextColumn(); ImGui::Checkbox("No menu", &no_menu); - ImGui::TableNextColumn(); ImGui::Checkbox("No move", &no_move); - ImGui::TableNextColumn(); ImGui::Checkbox("No resize", &no_resize); - ImGui::TableNextColumn(); ImGui::Checkbox("No collapse", &no_collapse); - ImGui::TableNextColumn(); ImGui::Checkbox("No close", &no_close); - ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav); - ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background); - ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front); - ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No titlebar", &no_titlebar)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No scrollbar", &no_scrollbar)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No menu", &no_menu)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No move", &no_move)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No resize", &no_resize)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No collapse", &no_collapse)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No close", &no_close)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No nav", &no_nav)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No background", &no_background)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("No bring to front", &no_bring_to_front)) ImGui::SetNextRefresh(0, "Window options changed"); + ImGui::TableNextColumn(); if (ImGui::Checkbox("Unsaved document", &unsaved_document)) ImGui::SetNextRefresh(0, "Window options changed"); ImGui::EndTable(); } } @@ -1661,7 +1661,9 @@ static void ShowDemoWindowWidgets() phase += 0.10f * values_offset; refresh_time += 1.0f / 60.0f; } - + if (animate) + ImGui::SetNextRefresh(float(refresh_time - ImGui::GetTime()), "Plot Animation"); + // Plots can display overlay texts // (in this example, we will display an average value) { diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 407ae51c441b..c00cefb05f7a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4705,11 +4705,30 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (render_cursor) { state->CursorAnim += io.DeltaTime; - bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; + ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); - if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + + bool cursor_is_visible = cursor_screen_rect.Overlaps(clip_rect); + if (cursor_is_visible) + { + if (g.IO.ConfigInputTextCursorBlink) + { + float next_refresh_time = -state->CursorAnim; + if (state->CursorAnim > 0.0f) + { + float modded = ImFmod(state->CursorAnim, 1.20f); + bool now_visible_blink_phase = modded <= 0.80f; + state->CursorAnim = modded; //prevent fp error from accumulation + next_refresh_time = now_visible_blink_phase ? 0.80f - modded : 1.20f - modded; + cursor_is_visible = now_visible_blink_phase; + } + g.IO.SetNextRefresh( next_refresh_time, "text cursor blink"); + } + + if (cursor_is_visible) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + } // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) if (!is_readonly) From cb5b60f2a27e35457c5854d2f8448683e5e80680 Mon Sep 17 00:00:00 2001 From: Sergey Nenakhov Date: Mon, 21 Mar 2022 10:48:29 +0100 Subject: [PATCH 2/2] Updated to 1.88 version (current latest) Key features: - frame is rendered only when it needs to render - reason for frame render is shown in console - fixed spurious mouse events resulting in needles updates - fixed dpi handling (and made dpi-enabled as default) - fixed plot animations - added api function (ImGui::SetNextRefresh) - fixed windows not redrawing when closing via close button - fixed scrollbar resize when container window grows --- backends/imgui_impl_win32.cpp | 4 ++-- imgui.cpp | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 570e468697cc..6603cae2b3d5 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -359,8 +359,8 @@ bool ImGui_ImplWin32_NewFrame(bool poll_only) double cur_delta = double(current_time - bd->Time) / bd->TicksPerSecond; if (cur_delta <= next_refresh) { - double ms_to_wait_double = (next_refresh - cur_delta) * 1000.0f; - unsigned int ms_to_wait = ms_to_wait_double >= MAXDWORD ? INFINITE : unsigned int(ms_to_wait_double); + double ms_to_wait_double = (next_refresh - cur_delta) * 1000.0f; + unsigned int ms_to_wait = ms_to_wait_double >= MAXDWORD ? INFINITE : unsigned int(ms_to_wait_double >= 0.0 ? ms_to_wait_double : 0.0); if (ms_to_wait) MsgWaitForMultipleObjectsEx(0, nullptr, ms_to_wait, QS_ALLEVENTS, 0); } diff --git a/imgui.cpp b/imgui.cpp index 921648a333c7..d49c94dce90a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6682,6 +6682,15 @@ void ImGui::End() if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging LogFinish(); + //fix against scrollbar size change: https://github.com/ocornut/imgui/pull/5116#issuecomment-1073457456 + if ((window->ScrollbarX || window->ScrollbarY) && g.IO.NextRefresh > 0) + { + ImVec2 sizes, sizes_ideal; + CalcWindowContentSizes(window, &sizes, &sizes_ideal); + if ((window->ScrollbarX && window->ContentSize.x != sizes.x) || (window->ScrollbarY && window->ContentSize.y != sizes.y)) + g.IO.SetNextRefresh(0, "scrollbar resize"); + } + // Pop from window stack g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; if (window->Flags & ImGuiWindowFlags_ChildMenu)