diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index d3b7d50b..f3e6b2d1 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -43,7 +43,7 @@ jobs: cd examples/example_null pvs-studio-analyzer trace -- make WITH_EXTRA_WARNINGS=1 pvs-studio-analyzer analyze -e ../../imstb_rectpack.h -e ../../imstb_textedit.h -e ../../imstb_truetype.h -l ../../pvs-studio.lic -o pvs-studio.log - plog-converter -a 'GA:1,2;OP:1' -t errorfile -w pvs-studio.log + plog-converter -a 'GA:1,2;OP:1' -d V1071 -t errorfile -w pvs-studio.log Discord-CI: runs-on: ubuntu-18.04 diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index e3733a08..808d099d 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -9,9 +9,13 @@ // Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. // This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. -// This define is set in the example .vcxproj file and need to be replicated in your app or by adding it to your imconfig.h file. +// To build this on 32-bit systems: +// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file) +// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. +// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) +// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file) -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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 @@ -326,7 +330,7 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL if (r.right > r.left && r.bottom > r.top) { D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {}; - texture_handle.ptr = (UINT64)(intptr_t)pcmd->GetTexID(); + texture_handle.ptr = (UINT64)pcmd->GetTexID(); ctx->SetGraphicsRootDescriptorTable(1, texture_handle); ctx->RSSetScissorRects(1, &r); ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); @@ -483,6 +487,13 @@ static void ImGui_ImplDX12_CreateFontsTexture() } // Store our identifier + // READ THIS IF THE STATIC_ASSERT() TRIGGERS: + // - Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. + // - This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. + // [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file) + // [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. + // [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) + // [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file) static_assert(sizeof(ImTextureID) >= sizeof(g_hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); io.Fonts->SetTexID((ImTextureID)g_hFontSrvGpuDescHandle.ptr); } diff --git a/backends/imgui_impl_dx9.cpp b/backends/imgui_impl_dx9.cpp index 0d3375eb..cbf1c056 100644 --- a/backends/imgui_impl_dx9.cpp +++ b/backends/imgui_impl_dx9.cpp @@ -6,7 +6,7 @@ // [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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 @@ -297,8 +297,8 @@ static bool ImGui_ImplDX9_CreateFontsTexture() #ifndef IMGUI_USE_BGRA_PACKED_COLOR if (io.Fonts->TexPixelsUseColors) { - ImU32* dst_start = (ImU32*)ImGui::MemAlloc(width * height * bytes_per_pixel); - for (ImU32* src = (ImU32*)pixels, *dst = dst_start, *dst_end = dst_start + width * height; dst < dst_end; src++, dst++) + ImU32* dst_start = (ImU32*)ImGui::MemAlloc((size_t)width * height * bytes_per_pixel); + for (ImU32* src = (ImU32*)pixels, *dst = dst_start, *dst_end = dst_start + (size_t)width * height; dst < dst_end; src++, dst++) *dst = IMGUI_COL_TO_DX9_ARGB(*src); pixels = (unsigned char*)dst_start; } @@ -312,7 +312,7 @@ static bool ImGui_ImplDX9_CreateFontsTexture() if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK) return false; for (int y = 0; y < height; y++) - memcpy((unsigned char*)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel)); + memcpy((unsigned char*)tex_locked_rect.pBits + (size_t)tex_locked_rect.Pitch * y, pixels + (size_t)width * bytes_per_pixel * y, (size_t)width * bytes_per_pixel); g_FontTexture->UnlockRect(0); // Store our identifier diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 7ec5a4f6..28c881cc 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -8,7 +8,7 @@ // [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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 @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2021-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader. // 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version. // 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) // 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater. @@ -157,7 +158,7 @@ using namespace gl; static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2) static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings. static GLuint g_FontTexture = 0; -static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; +static GLuint g_ShaderHandle = 0; static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; @@ -704,23 +705,29 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() // Create shaders const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader }; - g_VertHandle = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL); - glCompileShader(g_VertHandle); - CheckShader(g_VertHandle, "vertex shader"); + GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL); + glCompileShader(vert_handle); + CheckShader(vert_handle, "vertex shader"); const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader }; - g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL); - glCompileShader(g_FragHandle); - CheckShader(g_FragHandle, "fragment shader"); + GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL); + glCompileShader(frag_handle); + CheckShader(frag_handle, "fragment shader"); + // Link g_ShaderHandle = glCreateProgram(); - glAttachShader(g_ShaderHandle, g_VertHandle); - glAttachShader(g_ShaderHandle, g_FragHandle); + glAttachShader(g_ShaderHandle, vert_handle); + glAttachShader(g_ShaderHandle, frag_handle); glLinkProgram(g_ShaderHandle); CheckProgram(g_ShaderHandle, "shader program"); + glDetachShader(g_ShaderHandle, vert_handle); + glDetachShader(g_ShaderHandle, frag_handle); + glDeleteShader(vert_handle); + glDeleteShader(frag_handle); + g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position"); @@ -747,10 +754,6 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() { if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; } if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; } - if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); } - if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); } - if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; } - if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; } if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; } ImGui_ImplOpenGL3_DestroyFontsTexture(); diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index d2bbf0d4..0b7f8bff 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -9,7 +9,7 @@ // [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters].. // [ ] Platform: Multi-viewport / platform windows. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. // 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 @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2021-06-23: Inputs: Added a fix for shortcuts using CTRL key instead of CMD key. // 2021-04-19: Inputs: Added a fix for keys remaining stuck in pressed state when CMD-tabbing into different application. // 2021-01-27: Inputs: Added a fix for mouse position not being reported when mouse buttons other than left one are down. // 2020-10-28: Inputs: Added a fix for handling keypad-enter key. @@ -304,12 +305,12 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) for (NSUInteger i = 0; i < len; i++) { int c = [str characterAtIndex:i]; - if (!io.KeyCtrl && !(c >= 0xF700 && c <= 0xFFFF) && c != 127) + if (!io.KeySuper && !(c >= 0xF700 && c <= 0xFFFF) && c != 127) io.AddInputCharacter((unsigned int)c); // We must reset in case we're pressing a sequence of special keys while keeping the command pressed int key = mapCharacterToKey(c); - if (key != -1 && key < 256 && !io.KeyCtrl) + if (key != -1 && key < 256 && !io.KeySuper) resetKeys(); if (key != -1) io.KeysDown[key] = true; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 89c70160..c867f1e0 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -507,6 +507,8 @@ static BOOL _IsWindowsVersionOrGreater(WORD major, WORD minor, WORD) if (RtlVerifyVersionInfoFn == NULL) if (HMODULE ntdllModule = ::GetModuleHandleA("ntdll.dll")) RtlVerifyVersionInfoFn = (PFN_RtlVerifyVersionInfo)GetProcAddress(ntdllModule, "RtlVerifyVersionInfo"); + if (RtlVerifyVersionInfoFn == NULL) + return FALSE; RTL_OSVERSIONINFOEXW versionInfo = { }; ULONGLONG conditionMask = 0; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 49ec2864..2fffbdf4 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -104,21 +104,30 @@ Other changes: ----------------------------------------------------------------------- Other Changes: -- TabBar: Fixed using more than 32 KB-worth of tab names. (#4176) +- Windows: ImGuiWindowFlags_UnsavedDocument/ImGuiTabItmeFlags_UnsavedDocument display a dot instead of a '*' so it + is independent from font style. When in a tab, the dot is displayed at the same position as the close button. + Added extra comments to clarify the purpose of this flag in the context of docked windows. - Tables: Added ImGuiTableColumnFlags_Disabled acting a master disable over (hidden from user/context menu). (#3935) - Tables: Clarified that TableSetColumnEnabled() requires the table to use the ImGuiTableFlags_Hideable flag, because it manipulates the user-accessible show/hide state. (#3935) - Tables: Added ImGuiTableColumnFlags_NoHeaderLabel to request TableHeadersRow() to not submit label for a column. Convenient for some small columns. Name will still appear in context menu. (#4206). - Tables: Fix columns order on TableSetupScrollFreeze() if previous data got frozen columns out of their section. +- Tables: Fix invalid data in TableGetSortSpecs() when SpecsDirty flag is unset. (#4233) +- TabBar: Fixed using more than 32 KB-worth of tab names. (#4176) - Fixed printf-style format checks on non-MinGW flavors. (#4183, #3592) +- Fonts: Functions with a 'float size_pixels' parameter can accept zero if it is set in ImFontSize::SizePixels. - Demo: Fixed requirement in 1.83 to link with imgui_demo.cpp if IMGUI_DISABLE_METRICS_WINDOW is not set. (#4171) Normally the right way to disable compiling the demo is to set IMGUI_DISABLE_DEMO_WINDOWS, but we want to avoid implying that the file is required. - Backends: Win32: Rework to handle certains Windows 8.1/10 features without a manifest. (#4200, #4191) - ImGui_ImplWin32_GetDpiScaleForMonitor() will handle per-monitor DPI on Windows 10 without a manifest. - ImGui_ImplWin32_EnableDpiAwareness() will call SetProcessDpiAwareness() fallback on Windows 8.1 without a manifest. +- Backends: DX12: Fix texture casting crash on 32-bit systems (introduced on 2021/05/19 and v1.83) + added comments + about building on 32-bit systems. (#4225) [@kingofthebongo2008] - Backends: OpenGL3: Handle GL_CLIP_ORIGIN on <4.5 contexts if "GL_ARB_clip_control" extension is detected. (#4170, #3998) +- Backends: OpenGL3: Destroy vertex/fragment shader objects right after they are linked into main shader. (#4244) [@Crowbarous] +- Backends: OSX: Added a fix for shortcuts using CTRL key instead of CMD key. (#4253) [@rokups] - Examples: Updated all .vcxproj to VS2015 (toolset v140) to facilitate usage with vcpkg. - Examples: SDL2: Accomodate for vcpkg install having headers in SDL2/SDL.h vs SDL.h. diff --git a/docs/TODO.txt b/docs/TODO.txt index 1e59ffaf..3d611fd6 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -212,7 +212,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - combo: use clipper: make it easier to disable clipper with a single flag. - combo: flag for BeginCombo to not return true when unchanged (#1182) - - combo: a way/helper to customize the combo preview (#1658) + - combo: a way/helper to customize the combo preview (#1658) -> exeperimental BeginComboPreview() - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203) - listbox: multiple selection. - listbox: unselect option (#1208) diff --git a/imconfig.h b/imconfig.h index e0a2155a..769e7391 100644 --- a/imconfig.h +++ b/imconfig.h @@ -45,6 +45,7 @@ //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). +//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available //---- Include imgui_user.h at the end of imgui.h as a convenience //#define IMGUI_INCLUDE_IMGUI_USER_H diff --git a/imgui.cpp b/imgui.cpp index 9d208c36..d5d07e19 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6023,8 +6023,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker) // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code.. - const char* UNSAVED_DOCUMENT_MARKER = "*"; - const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? CalcTextSize(UNSAVED_DOCUMENT_MARKER, NULL, false).x : 0.0f; + const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f; const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f); // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button, @@ -6043,15 +6042,20 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y); ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y); - //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] - //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] - RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); if (flags & ImGuiWindowFlags_UnsavedDocument) { - ImVec2 marker_pos = ImVec2(ImMax(layout_r.Min.x, layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x) + text_size.x, layout_r.Min.y) + ImVec2(2 - marker_size_x, 0.0f); - ImVec2 off = ImVec2(0.0f, IM_FLOOR(-g.FontSize * 0.25f)); - RenderTextClipped(marker_pos + off, layout_r.Max + off, UNSAVED_DOCUMENT_MARKER, NULL, NULL, ImVec2(0, style.WindowTitleAlign.y), &clip_r); + ImVec2 marker_pos; + marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x); + marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f; + if (marker_pos.x > layout_r.Min.x) + { + RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text)); + clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f)); + } } + //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] + //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG] + RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r); } void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) @@ -6808,7 +6812,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Pull/inherit current state g.CurrentItemFlags = g.ItemFlagsStack.back(); // Inherit from shared stack - window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : 0; // Inherit from parent only // -V595 + window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595 if (!(flags & ImGuiWindowFlags_DockNodeHost)) PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); diff --git a/imgui.h b/imgui.h index c7bca906..4d755d92 100644 --- a/imgui.h +++ b/imgui.h @@ -62,7 +62,7 @@ Index of this file: // Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) #define IMGUI_VERSION "1.84 WIP" -#define IMGUI_VERSION_NUM 18307 +#define IMGUI_VERSION_NUM 18308 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_HAS_TABLE #define IMGUI_HAS_VIEWPORT // Viewport WIP branch @@ -974,7 +974,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) - ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. When used in a tab/docking context, tab is selected on closure and closure is deferred by one frame to allow code to cancel the closure (with a confirmation popup, etc.) without flicker. + ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiWindowFlags_NoDocking = 1 << 21, // Disable docking of this window ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, @@ -1115,7 +1115,7 @@ enum ImGuiTabBarFlags_ enum ImGuiTabItemFlags_ { ImGuiTabItemFlags_None = 0, - ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker. + ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() @@ -2568,6 +2568,7 @@ struct ImDrawList IMGUI_API void _ResetForNewFrame(); IMGUI_API void _ClearFreeMemory(); IMGUI_API void _PopUnusedDrawCmd(); + IMGUI_API void _TryMergeDrawCmds(); IMGUI_API void _OnChangedClipRect(); IMGUI_API void _OnChangedTextureID(); IMGUI_API void _OnChangedVtxOffset(); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 11b2a0ea..b83ad345 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -332,6 +332,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_background = false; static bool no_bring_to_front = false; static bool no_docking = false; + static bool unsaved_document = false; ImGuiWindowFlags window_flags = 0; if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; @@ -344,6 +345,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (no_background) window_flags |= ImGuiWindowFlags_NoBackground; if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking; + if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument; if (no_close) p_open = NULL; // Don't pass our bool* to Begin // We specify a default position/size in case there's no data in the .ini file. @@ -561,6 +563,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background); ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front); ImGui::TableNextColumn(); ImGui::Checkbox("No docking", &no_docking); + ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document); ImGui::EndTable(); } } @@ -1078,8 +1081,8 @@ static void ShowDemoWindowWidgets() // stored in the object itself, etc.) const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; static int item_current_idx = 0; // Here we store our selection data as an index. - const char* combo_label = items[item_current_idx]; // Label to preview before opening the combo (technically it could be anything) - if (ImGui::BeginCombo("combo 1", combo_label, flags)) + const char* combo_preview_value = items[item_current_idx]; // Pass in the preview value visible before opening the combo (it could be anything) + if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) { for (int n = 0; n < IM_ARRAYSIZE(items); n++) { @@ -1095,10 +1098,12 @@ static void ShowDemoWindowWidgets() } // Simplified one-liner Combo() API, using values packed in a single constant string + // This is a convenience for when the selection set is small and known at compile-time. static int item_current_2 = 0; ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); // Simplified one-liner Combo() using an array of const char* + // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control. static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); @@ -1165,7 +1170,7 @@ static void ShowDemoWindowWidgets() static bool selection[5] = { false, true, false, false, false }; ImGui::Selectable("1. I am selectable", &selection[0]); ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("3. I am not selectable"); + ImGui::Text("(I am not selectable)"); ImGui::Selectable("4. I am selectable", &selection[3]); if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) if (ImGui::IsMouseDoubleClicked(0)) @@ -6775,6 +6780,7 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::BeginChild("left pane", ImVec2(150, 0), true); for (int i = 0; i < 100; i++) { + // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav char label[128]; sprintf(label, "MyObject %d", i); if (ImGui::Selectable(label, selected == i)) @@ -7694,6 +7700,16 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::Separator(); + // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags. + // They have multiple effects: + // - Display a dot next to the title. + // - Tab is selected when clicking the X close button. + // - Closure is not assumed (will wait for user to stop submitting the tab). + // Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + // We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty + // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window. + // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole. + // Tabs if (opt_target == Target_Tab) { diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 2f852c71..c161bff3 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -497,6 +497,18 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) #define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset #define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset +// Try to merge two last draw commands +void ImDrawList::_TryMergeDrawCmds() +{ + ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; + ImDrawCmd* prev_cmd = curr_cmd - 1; + if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL) + { + prev_cmd->ElemCount += curr_cmd->ElemCount; + CmdBuffer.pop_back(); + } +} + // Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. // The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. void ImDrawList::_OnChangedClipRect() @@ -699,10 +711,11 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c } // On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds. -// Those macros expects l-values. -#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } while (0) +// - Those macros expects l-values and need to be used as their own statement. +// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers. +#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0 #define IM_FIXNORMAL2F_MAX_INVLEN2 100.0f // 500.0f (see #4053, #3366) -#define IM_FIXNORMAL2F(VX,VY) do { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } while (0) +#define IM_FIXNORMAL2F(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0 // TODO: Thickness anti-aliased lines cap are missing their AA fringe. // We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds. @@ -2160,7 +2173,7 @@ ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float si IM_ASSERT(font_cfg.FontData == NULL); font_cfg.FontData = ttf_data; font_cfg.FontDataSize = ttf_size; - font_cfg.SizePixels = size_pixels; + font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels; if (glyph_ranges) font_cfg.GlyphRanges = glyph_ranges; return AddFont(&font_cfg); diff --git a/imgui_internal.h b/imgui_internal.h index b7b28bd6..11a64ab4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -52,7 +52,7 @@ Index of this file: #include // INT_MIN, INT_MAX // Enable SSE intrinsics if available -#if defined __SSE__ || defined __x86_64__ || defined _M_X64 +#if (defined __SSE__ || defined __x86_64__ || defined _M_X64) && !defined(IMGUI_DISABLE_SSE) #define IMGUI_ENABLE_SSE #include #endif @@ -820,6 +820,12 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease }; +// Extend ImGuiComboFlags_ +enum ImGuiComboFlagsPrivate_ +{ + ImGuiComboFlags_CustomPreview = 1 << 20 // enable BeginComboPreview() +}; + // Extend ImGuiSliderFlags_ enum ImGuiSliderFlagsPrivate_ { @@ -832,12 +838,13 @@ enum ImGuiSelectableFlagsPrivate_ { // NB: need to be in sync with last value of ImGuiSelectableFlags_ ImGuiSelectableFlags_NoHoldingActiveID = 1 << 20, - ImGuiSelectableFlags_SelectOnClick = 1 << 21, // Override button behavior to react on Click (default is Click+Release) - ImGuiSelectableFlags_SelectOnRelease = 1 << 22, // Override button behavior to react on Release (default is Click+Release) - ImGuiSelectableFlags_SpanAvailWidth = 1 << 23, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) - ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 24, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. - ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) - ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26 // Disable padding each side with ItemSpacing * 0.5f + ImGuiSelectableFlags_SelectOnNav = 1 << 21, // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API. + ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) + ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) + ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) + ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. + ImGuiSelectableFlags_SetNavIdOnHover = 1 << 26, // Set Nav/Focus ID on mouse hover (used by MenuItem) + ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27 // Disable padding each side with ItemSpacing * 0.5f }; // Extend ImGuiTreeNodeFlags_ @@ -1008,6 +1015,19 @@ struct ImGuiStyleMod ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } }; +// Storage data for BeginComboPreview()/EndComboPreview() +struct IMGUI_API ImGuiComboPreviewData +{ + ImRect PreviewRect; + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + ImVec2 BackupCursorPosPrevLine; + float BackupPrevLineTextBaseOffset; + ImGuiLayoutType BackupLayout; + + ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); } +}; + // Stacked storage data for BeginGroup()/EndGroup() struct IMGUI_API ImGuiGroupData { @@ -1729,6 +1749,7 @@ struct ImGuiContext float ColorEditLastSat; // Backup of last Saturation associated to LastColor[3], so we can restore Saturation in lossy RGB<>HSV round trips float ColorEditLastColor[3]; ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. + ImGuiComboPreviewData ComboPreviewData; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? bool DragCurrentAccumDirty; @@ -2387,6 +2408,8 @@ struct ImGuiTable ImGuiWindow* InnerWindow; // Window holding the table data (== OuterWindow or a child window) ImGuiTextBuffer ColumnsNames; // Contiguous buffer holding columns names ImDrawListSplitter* DrawSplitter; // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly + ImGuiTableColumnSortSpecs SortSpecsSingle; + ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. ImGuiTableSortSpecs SortSpecs; // Public facing sorts specs, this is what we return in TableGetSortSpecs() ImGuiTableColumnIdx SortSpecsCount; ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) @@ -2436,7 +2459,6 @@ struct ImGuiTable // Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). // - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. // - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. -// FIXME-TABLE: more transient data could be stored here: DrawSplitter, incoming RowData? struct ImGuiTableTempData { int TableIndex; // Index in g.Tables.Buf[] pool @@ -2444,8 +2466,6 @@ struct ImGuiTableTempData ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() ImDrawListSplitter DrawSplitter; - ImGuiTableColumnSortSpecs SortSpecsSingle; - ImVector SortSpecsMulti; // FIXME-OPT: Using a small-vector pattern would be good. ImRect HostBackupWorkRect; // Backup of InnerWindow->WorkRect at the end of BeginTable() ImRect HostBackupParentWorkRect; // Backup of InnerWindow->ParentWorkRect at the end of BeginTable() @@ -2641,6 +2661,8 @@ namespace ImGui // Combos IMGUI_API bool BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags); + IMGUI_API bool BeginComboPreview(); + IMGUI_API void EndComboPreview(); // Gamepad/Keyboard Navigation IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); @@ -2966,11 +2988,7 @@ extern void ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, #define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA)); #define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) if (g.TestEngineHookIdInfo == _ID) ImGuiTestEngineHook_IdInfo(&g, _TYPE, _ID, (const void*)(_DATA), (const void*)(_DATA2)); #else -#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) do { } while (0) -#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) do { } while (0) -#define IMGUI_TEST_ENGINE_LOG(_FMT,...) do { } while (0) -#define IMGUI_TEST_ENGINE_ID_INFO(_ID,_TYPE,_DATA) do { } while (0) -#define IMGUI_TEST_ENGINE_ID_INFO2(_ID,_TYPE,_DATA,_DATA2) do { } while (0) +#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)0) #endif //----------------------------------------------------------------------------- diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 5cc88d66..06235155 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -2608,8 +2608,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() if (!table->IsLayoutLocked) TableUpdateLayout(table); - if (table->IsSortSpecsDirty) - TableSortSpecsBuild(table); + TableSortSpecsBuild(table); return &table->SortSpecs; } @@ -2748,14 +2747,18 @@ void ImGui::TableSortSpecsSanitize(ImGuiTable* table) void ImGui::TableSortSpecsBuild(ImGuiTable* table) { - IM_ASSERT(table->IsSortSpecsDirty); - TableSortSpecsSanitize(table); + bool dirty = table->IsSortSpecsDirty; + if (dirty) + { + TableSortSpecsSanitize(table); + table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); + table->SortSpecs.SpecsDirty = true; // Mark as dirty for user + table->IsSortSpecsDirty = false; // Mark as not dirty for us + } // Write output - ImGuiTableTempData* temp_data = table->TempData; - temp_data->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount); - ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &temp_data->SortSpecsSingle : temp_data->SortSpecsMulti.Data; - if (sort_specs != NULL) + ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data; + if (dirty && sort_specs != NULL) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { ImGuiTableColumn* column = &table->Columns[column_n]; @@ -2768,10 +2771,9 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder; sort_spec->SortDirection = column->SortDirection; } + table->SortSpecs.Specs = sort_specs; table->SortSpecs.SpecsCount = table->SortSpecsCount; - table->SortSpecs.SpecsDirty = true; // Mark as dirty for user - table->IsSortSpecsDirty = false; // Mark as not dirty for us } //------------------------------------------------------------------------- @@ -2914,7 +2916,6 @@ void ImGui::TableHeader(const char* label) const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } else { @@ -2922,6 +2923,7 @@ void ImGui::TableHeader(const char* label) if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); if (held) table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; @@ -3460,7 +3462,8 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) ImGuiContext& g = *GImGui; IM_ASSERT(table->MemoryCompacted == false); table->SortSpecs.Specs = NULL; - table->IsSortSpecsDirty = true; + table->SortSpecsMulti.clear(); + table->IsSortSpecsDirty = true; // FIXME: shouldn't have to leak into user performing a sort table->ColumnsNames.clear(); table->MemoryCompacted = true; for (int n = 0; n < table->ColumnsCount; n++) @@ -3471,7 +3474,6 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data) { temp_data->DrawSplitter.ClearFreeMemory(); - temp_data->SortSpecsMulti.clear(); temp_data->LastTimeActive = -1.0f; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ef0cd287..272160d8 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1450,6 +1450,8 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImRect bb_interact = bb; bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); + if (hovered) + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb if (g.ActiveId != id) SetItemAllowOverlap(); @@ -1547,6 +1549,8 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc // - BeginCombo() // - BeginComboPopup() [Internal] // - EndCombo() +// - BeginComboPreview() [Internal] +// - EndComboPreview() [Internal] // - Combo() //------------------------------------------------------------------------- @@ -1608,6 +1612,14 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF } RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding); + // Custom preview + if (flags & ImGuiComboFlags_CustomPreview) + { + g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y); + IM_ASSERT(preview_value == NULL || preview_value[0] == 0); + preview_value = NULL; + } + // Render preview and label if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) { @@ -1689,6 +1701,57 @@ void ImGui::EndCombo() EndPopup(); } +// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements +// (Experimental, see GitHub issues: #1658, #4168) +bool ImGui::BeginComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + if (window->SkipItems || !window->ClipRect.Overlaps(window->DC.LastItemRect)) // FIXME: Because we don't have a ImGuiItemStatusFlags_Visible flag to test last ItemAdd() result + return false; + IM_ASSERT(window->DC.LastItemRect.Min.x == preview_data->PreviewRect.Min.x && window->DC.LastItemRect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? + if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional) + return false; + + // FIXME: This could be contained in a PushWorkRect() api + preview_data->BackupCursorPos = window->DC.CursorPos; + preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos; + preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; + preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; + preview_data->BackupLayout = window->DC.LayoutType; + window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding; + window->DC.CursorMaxPos = window->DC.CursorPos; + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true); + + return true; +} + +void ImGui::EndComboPreview() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; + + // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future + ImDrawList* draw_list = window->DrawList; + if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y) + if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command + { + draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect; + draw_list->_TryMergeDrawCmds(); + } + PopClipRect(); + window->DC.CursorPos = preview_data->BackupCursorPos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos); + window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine; + window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset; + window->DC.LayoutType = preview_data->BackupLayout; + preview_data->PreviewRect = ImRect(); +} + // Getter for the old Combo() API: const char*[] static bool Items_ArrayGetter(void* data, int idx, const char** out_text) { @@ -5889,8 +5952,8 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l { const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); - RenderNavHighlight(frame_bb, id, nav_highlight_flags); } + RenderNavHighlight(frame_bb, id, nav_highlight_flags); if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) @@ -6127,6 +6190,17 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + // Auto-select when moved into + // - This will be more fully fleshed in the range-select branch + // - This is not exposed as it won't nicely work with some user side handling of shift/control + // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons + // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) + // - (2) usage will fail with clipped items + // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent) + if (g.NavJustMovedToId == id) + selected = pressed = true; + // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) { @@ -6153,8 +6227,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); if (span_all_columns && window->DC.CurrentColumns) PopColumnsBackground(); @@ -8138,14 +8212,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, #endif // Render text label (with clipping + alpha gradient) + unsaved marker - const char* TAB_UNSAVED_MARKER = "*"; ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); - if (flags & ImGuiTabItemFlags_UnsavedDocument) - { - text_pixel_clip_bb.Max.x -= CalcTextSize(TAB_UNSAVED_MARKER, NULL, false).x; - ImVec2 unsaved_marker_pos(ImMin(bb.Min.x + frame_padding.x + label_size.x + 2, text_pixel_clip_bb.Max.x), bb.Min.y + frame_padding.y + IM_FLOOR(-g.FontSize * 0.25f)); - RenderTextClippedEx(draw_list, unsaved_marker_pos, bb.Max - frame_padding, TAB_UNSAVED_MARKER, NULL, NULL); - } ImRect text_ellipsis_clip_bb = text_pixel_clip_bb; // Return clipped state ignoring the close button @@ -8155,7 +8222,10 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); } - // Close Button + const float button_sz = g.FontSize; + const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x * 2.0f - button_sz), bb.Min.y); + + // Close Button & Unsaved Marker // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() // 'hovered' will be true when hovering the Tab but NOT when hovering the close button // 'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button @@ -8163,15 +8233,16 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, bool close_button_pressed = false; bool close_button_visible = false; if (close_button_id != 0) - if (is_contents_visible || bb.GetWidth() >= g.Style.TabMinWidthForCloseButton) + if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton)) if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id) close_button_visible = true; + bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x); + if (close_button_visible) { ImGuiLastItemDataBackup last_item_backup; - const float close_button_sz = g.FontSize; PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding); - if (CloseButton(close_button_id, ImVec2(bb.Max.x - frame_padding.x * 2.0f - close_button_sz, bb.Min.y))) + if (CloseButton(close_button_id, button_pos)) close_button_pressed = true; PopStyleVar(); last_item_backup.Restore(); @@ -8179,12 +8250,23 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, // Close with middle mouse button if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2)) close_button_pressed = true; - - text_pixel_clip_bb.Max.x -= close_button_sz; + } + else if (unsaved_marker_visible) + { + const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz) + g.Style.FramePadding * 2.0f); + RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); } + // This is all rather complicated + // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f; + if (close_button_visible || unsaved_marker_visible) + { + text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f); + text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f; + ellipsis_max_x = text_pixel_clip_bb.Max.x; + } RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size); #if 0 diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index e6221f4b..71a18870 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -41,7 +41,8 @@ #include FT_SYNTHESIS_H // #ifdef _MSC_VER -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif #if defined(__GNUC__) @@ -600,13 +601,15 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); if (src_load_color) { - atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight * 4); - memset(atlas->TexPixelsRGBA32, 0, atlas->TexWidth * atlas->TexHeight * 4); + size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 4; + atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(tex_size); + memset(atlas->TexPixelsRGBA32, 0, tex_size); } else { - atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight); - memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); + size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 1; + atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(tex_size); + memset(atlas->TexPixelsAlpha8, 0, tex_size); } // 8. Copy rasterized font characters back into the main texture @@ -645,6 +648,22 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u const int tx = pack_rect.x + padding; const int ty = pack_rect.y + padding; + // Register glyph + float x0 = info.OffsetX + font_off_x; + float y0 = info.OffsetY + font_off_y; + float x1 = x0 + info.Width; + float y1 = y0 + info.Height; + float u0 = (tx) / (float)atlas->TexWidth; + float v0 = (ty) / (float)atlas->TexHeight; + float u1 = (tx + info.Width) / (float)atlas->TexWidth; + float v1 = (ty + info.Height) / (float)atlas->TexHeight; + dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX); + + ImFontGlyph* dst_glyph = &dst_font->Glyphs.back(); + IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint); + if (src_glyph.Info.IsColored) + dst_glyph->Colored = tex_use_colors = true; + // Blit from temporary buffer to final texture size_t blit_src_stride = (size_t)src_glyph.Info.Width; size_t blit_dst_stride = (size_t)atlas->TexWidth; @@ -663,22 +682,6 @@ bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, u for (int x = 0; x < info.Width; x++) blit_dst[x] = blit_src[x]; } - - // Register glyph - float x0 = info.OffsetX + font_off_x; - float y0 = info.OffsetY + font_off_y; - float x1 = x0 + info.Width; - float y1 = y0 + info.Height; - float u0 = (tx) / (float)atlas->TexWidth; - float v0 = (ty) / (float)atlas->TexHeight; - float u1 = (tx + info.Width) / (float)atlas->TexWidth; - float v1 = (ty + info.Height) / (float)atlas->TexHeight; - dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX); - - ImFontGlyph* dst_glyph = &dst_font->Glyphs.back(); - IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint); - if (src_glyph.Info.IsColored) - dst_glyph->Colored = tex_use_colors = true; } src_tmp.Rects = NULL;