mirror of
https://github.com/ocornut/imgui.git
synced 2026-08-22 15:21:15 +00:00
Merge branch 'master' into docking
# Conflicts: # backends/imgui_impl_opengl3.cpp
This commit is contained in:
@@ -25,8 +25,10 @@
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
|
||||
// 2026-06-23: OpenGL: GLSL version detection assume GLSL 410 when GL context is 4.1. Fixes an issue running on macOS with Wine. (#9427, #6577)
|
||||
// 2026-06-17: OpenGL: Expose selected render state in ImGui_ImplOpenGL3_RenderState, Allowing to dynamically select between use of glBindSampler() and glTexParameter(). You can access in 'void* platform_io.Renderer_RenderState' during rendering.
|
||||
// 2026-06-03: OpenGL: GLSL version detection assume GLSL 410 when GL context is 4.1. Fixes an issue running on macOS with Wine. (#9427, #6577)
|
||||
// 2026-04-23: OpenGL: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378)
|
||||
// (Breaking): this change prioritize using glBindSampler() when available, which would override glTexParameter() settings you may have set on custom textures.
|
||||
// 2026-03-12: OpenGL: Fixed invalid assert in ImGui_ImplOpenGL3_UpdateTexture() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295)
|
||||
// 2025-12-11: OpenGL: Fixed embedded loader multiple init/shutdown cycles broken on some platforms. (#8792, #9112)
|
||||
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
|
||||
@@ -256,8 +258,6 @@ struct ImGui_ImplOpenGL3_Data
|
||||
bool HasBindSampler;
|
||||
bool HasClipOrigin;
|
||||
bool UseBufferSubData;
|
||||
bool UseTexParameterToSetSampler;
|
||||
GLuint NextSampler; // Used if !HasBindSampler && UseTexParameterToSetSampler.
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
GLuint TexSamplers[2]; // Used if HasBindSimpler. (0=linear, 1=nearest)
|
||||
#endif
|
||||
@@ -337,7 +337,7 @@ void ImGui_ImplOpenGL3_NewFrame()
|
||||
IM_ASSERT(0 && "ImGui_ImplOpenGL3_CreateDeviceObjects() failed!");
|
||||
}
|
||||
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, ImGui_ImplOpenGL3_RenderState* render_state, int fb_width, int fb_height, GLuint vertex_array_object)
|
||||
{
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
|
||||
@@ -391,8 +391,11 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
||||
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
if (bd->HasBindSampler)
|
||||
glBindSampler(0, bd->TexSamplers[0]); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise.
|
||||
if (render_state->UseBindSampler)
|
||||
{
|
||||
render_state->CurrentSampler = bd->TexSamplers[0];
|
||||
glBindSampler(0, render_state->CurrentSampler); // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise.
|
||||
}
|
||||
#endif
|
||||
|
||||
(void)vertex_array_object;
|
||||
@@ -413,13 +416,42 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
|
||||
|
||||
// Draw callbacks
|
||||
static void ImGui_ImplOpenGL3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way.
|
||||
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*)
|
||||
{
|
||||
ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState();
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->HasBindSampler) { glBindSampler(0, bd->TexSamplers[0]); } else { bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; } }
|
||||
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); if (bd->HasBindSampler) { glBindSampler(0, bd->TexSamplers[1]); } else { bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; } }
|
||||
#else
|
||||
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_LINEAR; }
|
||||
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); bd->UseTexParameterToSetSampler = true; bd->NextSampler = GL_NEAREST; }
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
if (bd->HasBindSampler)
|
||||
{
|
||||
render_state->CurrentSampler = bd->TexSamplers[0];
|
||||
render_state->UseTexParameterFilter = false;
|
||||
glBindSampler(0, render_state->CurrentSampler);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
render_state->UseTexParameterFilter = true;
|
||||
render_state->CurrentTexParameterFilter = GL_LINEAR;
|
||||
}
|
||||
}
|
||||
static void ImGui_ImplOpenGL3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*)
|
||||
{
|
||||
ImGui_ImplOpenGL3_RenderState* render_state = ImGui_ImplOpenGL3_GetRenderState();
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
if (bd->HasBindSampler)
|
||||
{
|
||||
render_state->CurrentSampler = bd->TexSamplers[1];
|
||||
render_state->UseTexParameterFilter = false;
|
||||
glBindSampler(0, render_state->CurrentSampler);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
render_state->UseTexParameterFilter = true;
|
||||
render_state->CurrentTexParameterFilter = GL_NEAREST;
|
||||
}
|
||||
}
|
||||
|
||||
// OpenGL3 Render function.
|
||||
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
|
||||
@@ -489,7 +521,17 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||
GL_CALL(glGenVertexArrays(1, &vertex_array_object));
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
|
||||
// Setup render state structure (for callbacks and custom texture bindings)
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
ImGui_ImplOpenGL3_RenderState render_state;
|
||||
render_state.UseBindSampler = bd->HasBindSampler;
|
||||
render_state.UseTexParameterFilter = false;
|
||||
render_state.CurrentSampler = 0;
|
||||
render_state.CurrentTexParameterFilter = 0;
|
||||
platform_io.Renderer_RenderState = &render_state;
|
||||
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, &render_state, fb_width, fb_height, vertex_array_object);
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
@@ -536,7 +578,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
if (pcmd->UserCallback == ImGui_ImplOpenGL3_DrawCallback_ResetRenderState)
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, &render_state, fb_width, fb_height, vertex_array_object);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
}
|
||||
@@ -555,11 +597,11 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()));
|
||||
|
||||
// Emulate sampler change (even though it is technically part of texture data)
|
||||
// As a sort of hack/workaround, we only start writing using glTextParameter() if sampler is ever changed explicitly.
|
||||
if (!bd->HasBindSampler && bd->UseTexParameterToSetSampler)
|
||||
// As a sort of hack/workaround, we only start writing using glTexParameter() if sampler is ever changed explicitly.
|
||||
if (render_state.UseTexParameterFilter)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bd->NextSampler);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, bd->NextSampler);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, render_state.CurrentTexParameterFilter);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, render_state.CurrentTexParameterFilter);
|
||||
}
|
||||
|
||||
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
@@ -571,6 +613,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
platform_io.Renderer_RenderState = nullptr;
|
||||
|
||||
// Destroy the temporary VAO
|
||||
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
|
||||
|
||||
@@ -66,4 +66,17 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex);
|
||||
|
||||
#endif
|
||||
|
||||
// [BETA] Selected render state data shared with callbacks.
|
||||
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplOpenGL3_RenderDrawData() call.
|
||||
// (Please open an issue if you feel you need access to more data)
|
||||
struct ImGui_ImplOpenGL3_RenderState
|
||||
{
|
||||
bool UseBindSampler;
|
||||
bool UseTexParameterFilter;
|
||||
unsigned int CurrentSampler; // (GLuint) Used if UseBindSampler == true, otherwise always 0
|
||||
unsigned int CurrentTexParameterFilter; // (GLuint) Used if UseTexParameterToSetSampler == true
|
||||
};
|
||||
|
||||
static inline ImGui_ImplOpenGL3_RenderState* ImGui_ImplOpenGL3_GetRenderState() { return (ImGui_ImplOpenGL3_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState; }
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
@@ -566,8 +566,17 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
|
||||
|
||||
// Draw callbacks
|
||||
static void ImGui_ImplVulkan_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way.
|
||||
static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr); }
|
||||
static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr); }
|
||||
static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->RenderState->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr); }
|
||||
static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->RenderState->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr); }
|
||||
|
||||
// If you want to use your own sampler, you can create your own callback, access ImGui_ImplVulkan_RenderState for command-buffer and pipeline layout, e.g.
|
||||
/*
|
||||
void ImGui_ImplVulkan_DrawCallback_SetSamplerCustom(const ImDrawList*, const ImDrawCmd* cmd)
|
||||
{
|
||||
ImGui_ImplVulkan_RenderState* render_state = (ImGui_ImplVulkan_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
|
||||
vkCmdBindDescriptorSets(render_state->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, render_state->PipelineLayout, 1, 1, (VkDescriptorSet*)cmd->UserCallbackData, 0, nullptr);
|
||||
}
|
||||
*/
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline)
|
||||
|
||||
@@ -175,6 +175,7 @@ IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_vers
|
||||
|
||||
// [BETA] Selected render state data shared with callbacks.
|
||||
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplVulkan_RenderDrawData() call.
|
||||
// ImGui_ImplVulkan_RenderState* render_state = (ImGui_ImplVulkan_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
|
||||
// (Please open an issue if you feel you need access to more data)
|
||||
struct ImGui_ImplVulkan_RenderState
|
||||
{
|
||||
|
||||
@@ -52,10 +52,26 @@ Other Changes:
|
||||
for user code to bypass it without using a clickable item. (#9382)
|
||||
- Clicking on a window's empty-space to move/focus a window checks
|
||||
for lack of queued focus request. (#9382)
|
||||
- Fixed double-click collapse toggle not owning the mouse button. If a
|
||||
`SetNextWindowPos()` with pivot was queued in the same frame, the second
|
||||
click could trigger another item in the same window. (#9439) [@Cleroth]
|
||||
- InputText:
|
||||
- Added `style.InputTextCursorSize` to configure cursor/caret thickness. (#7031, #9409)
|
||||
This is automatically scaled by `style.ScaleAllSizes()`.
|
||||
- Tables:
|
||||
- Redesigned/rewrote code to reconcile columns and settings on topology changes. (#9108)
|
||||
- When a column label is passed to TableSetupColumn(), the underlying identifier
|
||||
is used to match live columns data and .ini settings data when changing.
|
||||
This makes it possible to add/remove columns from a table without losing
|
||||
neither live data neither .ini settings data.
|
||||
- PS: Note that this is distinct from toggling column visibility or reordering
|
||||
columns, which was always possible. The new matching makes it easier to create
|
||||
tables that are entirely customized by user or code, without losing state.
|
||||
- Columns without identifiers or with duplicate identifiers are matched
|
||||
sequentially, matching old behavior.
|
||||
- Column ID are stored in .ini file.
|
||||
- Code is being tested both for live topology changes and for loading .ini
|
||||
data with mismatched topology.
|
||||
- Context Menu: added a "Reset" sub-menu with a "Reset Visibility" option.
|
||||
(which is greyed out when using default settings)
|
||||
- Headers: fixed label being clipped early to reserve space for a sort marker
|
||||
@@ -71,6 +87,9 @@ Other Changes:
|
||||
calling ClearFonts() during rendering.
|
||||
- Fixed an issue where passing a manually created ImFontAtlas to CreateContext() would
|
||||
incorrectly destroy it in DestroyContext() when ref-count gets back to zero. (#9426)
|
||||
- Nav:
|
||||
- Fixed context menu activation with gamepad erroneously testing for _NavEnableKeyboard
|
||||
instead of _NavEnableGamepad. (#9454, #8803, #9270) [@Clownacy]
|
||||
- DrawList:
|
||||
- Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091)
|
||||
- Added `ImDrawListFlags_TextNoPixelSnap` to disable snapping of AddText()
|
||||
@@ -86,6 +105,8 @@ Other Changes:
|
||||
- OpenGL3:
|
||||
- GLSL version detection assume GLSL 410 when GL context is 4.1.
|
||||
Fixes an issue running on macOS with Wine. [#9427, #6577) [@perminovVS]
|
||||
- Expose selected render state in ImGui_ImplOpenGL3_RenderState, allowing to
|
||||
dynamically select between use of glBindSampler() and glTexParameter(). (#9378)
|
||||
- Win32:
|
||||
- Uses `SetProcessDpiAwarenessContext()` instead of `SetThreadDpiAwarenessContext()`
|
||||
when available, fixing OpenGL DPI scaling issues as e.g. NVIDIA drivers tends
|
||||
@@ -152,6 +173,8 @@ Breaking Changes:
|
||||
- DirectX10, DirectX11, SDLGPU3, Vulkan: removed samplers from `ImGui_ImplXXXX_RenderState`.
|
||||
Prefer to use backend-agnostic DrawCallback_SetSamplerLinear which works everywhere! (#9378)
|
||||
If there is a legit need/request for them or any render state we can always add them back.
|
||||
- OpenGL3: the new sampler support system prioritize using glBindSampler() when available,
|
||||
which would override glTexParameter() settings you may have set on custom textures. (#9378)
|
||||
|
||||
Other Changes:
|
||||
|
||||
|
||||
@@ -5949,8 +5949,11 @@ void ImGui::NewFrame()
|
||||
if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
|
||||
TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
|
||||
for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
|
||||
{
|
||||
table_temp_data.ReconcileColumnsRequests.clear(); // Unusual: clear every frame because this is rarely used.
|
||||
if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
|
||||
TableGcCompactTransientBuffers(&table_temp_data);
|
||||
}
|
||||
if (g.GcCompactAll)
|
||||
GcCompactTransientMiscBuffers();
|
||||
g.GcCompactAll = false;
|
||||
@@ -8160,7 +8163,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
|
||||
ImRect title_bar_rect = window->TitleBarRect();
|
||||
if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
|
||||
if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
|
||||
{
|
||||
window->WantCollapseToggle = true;
|
||||
SetKeyOwner(ImGuiKey_MouseLeft, window->MoveId); // Claim input the same way ButtonBehavior() does. Prevent a same-frame move from triggering other items. (#9439)
|
||||
}
|
||||
if (window->WantCollapseToggle)
|
||||
{
|
||||
window->Collapsed = !window->Collapsed;
|
||||
@@ -15065,7 +15071,7 @@ static void ImGui::NavUpdateContextMenuRequest()
|
||||
ImGuiContext& g = *GImGui;
|
||||
g.NavOpenContextMenuItemId = g.NavOpenContextMenuWindowId = 0;
|
||||
const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
|
||||
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
|
||||
const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0;
|
||||
if ((!nav_keyboard_active && !nav_gamepad_active) || g.NavWindow == NULL)
|
||||
return;
|
||||
|
||||
|
||||
6
imgui.h
6
imgui.h
@@ -30,7 +30,7 @@
|
||||
// Library Version
|
||||
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
|
||||
#define IMGUI_VERSION "1.92.9 WIP"
|
||||
#define IMGUI_VERSION_NUM 19283
|
||||
#define IMGUI_VERSION_NUM 19284
|
||||
#define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
|
||||
#define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
|
||||
#define IMGUI_HAS_VIEWPORT // In 'docking' WIP branch.
|
||||
@@ -657,7 +657,7 @@ namespace ImGui
|
||||
// Widgets: Images
|
||||
// - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
|
||||
// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
|
||||
// - Image() pads adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side.
|
||||
// - Image() adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side.
|
||||
// - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified.
|
||||
// - An obsolete version of Image(), before 1.91.9 (March 2025), had a 'tint_col' parameter which is now supported by the ImageWithBg() function.
|
||||
IMGUI_API void Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1));
|
||||
@@ -817,7 +817,7 @@ namespace ImGui
|
||||
// - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar.
|
||||
// - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it.
|
||||
// - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it.
|
||||
// - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.
|
||||
// - Note that MenuItem() keyboard shortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.
|
||||
IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window).
|
||||
IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true!
|
||||
IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar.
|
||||
|
||||
@@ -3127,7 +3127,7 @@ struct IMGUI_API ImGuiTabBar
|
||||
#define IM_COL32_DISABLE IM_COL32(0,0,0,1) // Special sentinel code which cannot be used as a regular color.
|
||||
#define IMGUI_TABLE_MAX_COLUMNS 512 // Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
|
||||
|
||||
// [Internal] sizeof() ~ 112
|
||||
// [Internal] sizeof() ~ 120
|
||||
// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
|
||||
// We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping.
|
||||
// This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped".
|
||||
@@ -3168,7 +3168,11 @@ struct ImGuiTableColumn
|
||||
bool IsVisibleY;
|
||||
bool IsRequestOutput; // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not.
|
||||
bool IsSkipItems; // Do we want item submissions to this column to be completely ignored (no layout will happen).
|
||||
bool IsPreserveWidthAuto;
|
||||
bool IsPreserveWidthAuto : 1;
|
||||
bool IsJustCreated : 1;
|
||||
bool IsLoadedSettings : 1;
|
||||
bool IsNeedReconcileSrc : 1;
|
||||
bool IsNeedReconcileDst : 1;
|
||||
ImS8 NavLayerCurrent; // ImGuiNavLayer in 1 byte
|
||||
ImU8 AutoFitQueue : 4; // Queue of 4 values for the next 4 frames to request auto-fit
|
||||
ImU8 CannotSkipItemsQueue : 4; // Queue of 4 values for the next 4 frames to disable Clipped/SkipItem
|
||||
@@ -3186,10 +3190,28 @@ struct ImGuiTableColumn
|
||||
PrevEnabledColumn = NextEnabledColumn = -1;
|
||||
SortOrder = -1;
|
||||
SortDirection = ImGuiSortDirection_None;
|
||||
IsJustCreated = true;
|
||||
DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1;
|
||||
}
|
||||
};
|
||||
|
||||
// Passed to TableSetupColumn()
|
||||
// sizeof() ~ 24+120 bytes
|
||||
struct ImGuiTableReconcileColumnData
|
||||
{
|
||||
// Setup data
|
||||
ImGuiID ID;
|
||||
ImS16 NameOffset;
|
||||
ImGuiTableColumnFlags Flags;
|
||||
float InitWidthOrWeight;
|
||||
ImGuiID UserData;
|
||||
|
||||
// Reconcile data
|
||||
ImGuiTableColumnIdx ColumnNewIdx; // Index in the current table.
|
||||
ImGuiTableColumnIdx ColumnOldIdx; // Index in the previous frame table.
|
||||
ImGuiTableColumn ColumnOldData; // Full backup of the column. Could be avoided by storing 1 of them and applying reconcile in the right order. Not worth bothering.
|
||||
};
|
||||
|
||||
// Transient cell data stored per row.
|
||||
// sizeof() ~ 6 bytes
|
||||
struct ImGuiTableCellData
|
||||
@@ -3323,6 +3345,7 @@ struct IMGUI_API ImGuiTable
|
||||
bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row.
|
||||
bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow().
|
||||
bool IsInitializing;
|
||||
bool IsReconcileMode;
|
||||
bool IsSortSpecsDirty;
|
||||
bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag.
|
||||
bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
|
||||
@@ -3351,7 +3374,7 @@ struct IMGUI_API ImGuiTable
|
||||
// - 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 in a stacked ImGuiTableTempData: e.g. SortSpecs.
|
||||
// sizeof() ~ 136 bytes.
|
||||
// sizeof() ~ 176 bytes.
|
||||
struct IMGUI_API ImGuiTableTempData
|
||||
{
|
||||
ImGuiID WindowID; // Shortcut to g.Tables[TableIndex]->OuterWindow->ID.
|
||||
@@ -3360,6 +3383,11 @@ struct IMGUI_API ImGuiTableTempData
|
||||
float AngledHeadersExtraWidth; // Used in EndTable()
|
||||
ImVector<ImGuiTableHeaderData> AngledHeadersRequests; // Used in TableAngledHeadersRow() // FIXME: Single instance is enough?
|
||||
|
||||
// Topology change
|
||||
ImVector<ImGuiTableReconcileColumnData> ReconcileColumnsRequests; // Used in TableSetupColumn(), TableUpdateLayout(). Cleared every frame.
|
||||
void* OldColumnsRawData; // Used in BeginTable() -> TableUpdateLayout() when resizing.
|
||||
ImSpan<ImGuiTableColumn> OldColumnsData;
|
||||
|
||||
ImVec2 UserOuterSize; // outer_size.x passed to BeginTable()
|
||||
ImDrawListSplitter DrawSplitter;
|
||||
|
||||
@@ -3386,6 +3414,7 @@ struct ImGuiTableColumnSettings
|
||||
ImU8 SortDirection : 2;
|
||||
ImS8 IsEnabled : 2; // "Visible" in .ini file
|
||||
ImU8 IsStretch : 1;
|
||||
bool IsLoaded : 1; // Using during loading to mark finding a matching column.
|
||||
|
||||
ImGuiTableColumnSettings()
|
||||
{
|
||||
@@ -3396,6 +3425,7 @@ struct ImGuiTableColumnSettings
|
||||
SortDirection = ImGuiSortDirection_None;
|
||||
IsEnabled = -1;
|
||||
IsStretch = 0;
|
||||
IsLoaded = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3435,6 +3465,7 @@ namespace ImGui
|
||||
IMGUI_API void TableBeginInitMemory(ImGuiTable* table, int columns_count);
|
||||
IMGUI_API void TableApplyQueuedRequests(ImGuiTable* table);
|
||||
IMGUI_API void TableSetupDrawChannels(ImGuiTable* table);
|
||||
IMGUI_API void TableReconcileColumns(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateLayout(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateBorders(ImGuiTable* table);
|
||||
IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table);
|
||||
@@ -3448,6 +3479,7 @@ namespace ImGui
|
||||
IMGUI_API void TableFixDisplayOrder(ImGuiTable* table);
|
||||
IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table);
|
||||
IMGUI_API void TableSortSpecsBuild(ImGuiTable* table);
|
||||
IMGUI_API void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask);
|
||||
IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column);
|
||||
IMGUI_API void TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column);
|
||||
IMGUI_API float TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column);
|
||||
@@ -3471,6 +3503,7 @@ namespace ImGui
|
||||
// Tables: Settings
|
||||
IMGUI_API void TableLoadSettings(ImGuiTable* table);
|
||||
IMGUI_API void TableLoadSettingsForColumns(ImGuiTable* table);
|
||||
IMGUI_API void TableLoadSettingsForColumn(ImGuiTableColumn* column, ImGuiTableColumnSettings* column_settings, ImGuiTableFlags load_flags);
|
||||
IMGUI_API void TableSaveSettings(ImGuiTable* table);
|
||||
IMGUI_API void TableResetSettings(ImGuiTable* table);
|
||||
IMGUI_API ImGuiTableSettings* TableGetBoundSettings(ImGuiTable* table);
|
||||
|
||||
322
imgui_tables.cpp
322
imgui_tables.cpp
@@ -45,7 +45,6 @@ Index of this file:
|
||||
// - TableSetupScrollFreeze() user submit scroll freeze information (optional)
|
||||
//-----------------------------------------------------------------------------
|
||||
// - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().
|
||||
// | TableLoadSettingsForColumns() - on settings load
|
||||
// | TableApplyQueuedRequests() - apply queued resizing/reordering/hiding requests
|
||||
// | - TableSetColumnWidth() - apply resizing width (for mouse resize, often requested by previous frame)
|
||||
// | - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width
|
||||
@@ -364,6 +363,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData());
|
||||
ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1];
|
||||
temp_data->TableIndex = table_idx;
|
||||
temp_data->ReconcileColumnsRequests.resize(0); // FIXME: Use shrink(0) everywhere?
|
||||
table->DrawSplitter = &table->TempData->DrawSplitter;
|
||||
table->DrawSplitter->Clear();
|
||||
|
||||
@@ -375,6 +375,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
const int previous_frame_active = table->LastFrameActive;
|
||||
const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
|
||||
const ImGuiTableFlags previous_flags = table->Flags;
|
||||
const bool is_new_table = (previous_frame_active == -1);
|
||||
table->ID = id;
|
||||
table->Flags = flags;
|
||||
table->LastFrameActive = g.FrameCount;
|
||||
@@ -564,30 +565,36 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->MemoryCompacted = false;
|
||||
|
||||
// Setup memory buffer (clear data if columns count changed)
|
||||
ImGuiTableColumn* old_columns_to_preserve = NULL;
|
||||
void* old_columns_raw_data = NULL;
|
||||
const int old_columns_count = table->Columns.size();
|
||||
if (old_columns_count != 0 && old_columns_count != columns_count)
|
||||
{
|
||||
// Attempt to preserve width and other settings on column count/specs change (#4046)
|
||||
// Attempt to preserve width and other settings on column count/specs change (#4046, #9108)
|
||||
IMGUI_DEBUG_LOG_TABLE("[table] Table 0x%08X column count %d -> %d, recreating storage.\n", table->ID, old_columns_count, columns_count);
|
||||
old_columns_to_preserve = table->Columns.Data;
|
||||
old_columns_raw_data = table->RawData; // Free at end of function
|
||||
IM_ASSERT(temp_data->OldColumnsRawData == NULL);
|
||||
temp_data->OldColumnsRawData = table->RawData; // Freed during layout
|
||||
temp_data->OldColumnsData = table->Columns;
|
||||
for (ImGuiTableColumn& src_column : table->TempData->OldColumnsData)
|
||||
src_column.IsNeedReconcileSrc = true;
|
||||
table->RawData = NULL;
|
||||
}
|
||||
if (table->RawData == NULL)
|
||||
{
|
||||
TableBeginInitMemory(table, columns_count);
|
||||
table->IsInitializing = table->IsSettingsRequestLoad = true;
|
||||
table->IsInitializing = true;
|
||||
}
|
||||
if (table->IsResetAllRequest)
|
||||
TableResetSettings(table);
|
||||
if (table->IsInitializing)
|
||||
{
|
||||
// Initialize
|
||||
table->SettingsOffset = -1;
|
||||
if (is_new_table)
|
||||
{
|
||||
table->SettingsOffset = -1;
|
||||
table->IsSettingsRequestLoad = true;
|
||||
}
|
||||
table->IsSortSpecsDirty = true;
|
||||
table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
|
||||
table->IsReconcileMode = false;
|
||||
table->InstanceInteracted = -1;
|
||||
table->ContextPopupColumn = -1;
|
||||
table->ReorderColumn = table->ReorderColumnDstOrder = table->ResizedColumn = table->LastResizedColumn = -1;
|
||||
@@ -596,9 +603,9 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
for (int n = 0; n < columns_count; n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[n];
|
||||
if (old_columns_to_preserve && n < old_columns_count)
|
||||
if (temp_data->OldColumnsData.Data && n < temp_data->OldColumnsData.size())
|
||||
{
|
||||
*column = old_columns_to_preserve[n];
|
||||
*column = temp_data->OldColumnsData[n];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -612,8 +619,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->DisplayOrderToIndex[n] = column->DisplayOrder;
|
||||
}
|
||||
}
|
||||
if (old_columns_raw_data)
|
||||
IM_FREE(old_columns_raw_data);
|
||||
|
||||
// Load settings
|
||||
if (table->IsSettingsRequestLoad)
|
||||
@@ -843,7 +848,37 @@ static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, I
|
||||
void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTableTempData* temp_data = table->TempData;
|
||||
IM_ASSERT(table->IsLayoutLocked == false);
|
||||
const int columns_count = table->ColumnsCount;
|
||||
|
||||
// Reconcile moved columns
|
||||
if (temp_data->ReconcileColumnsRequests.Size > 0)
|
||||
TableReconcileColumns(table);
|
||||
if (temp_data->OldColumnsRawData)
|
||||
{
|
||||
IM_FREE(temp_data->OldColumnsRawData);
|
||||
temp_data->OldColumnsRawData = NULL;
|
||||
temp_data->OldColumnsData.clear();
|
||||
}
|
||||
|
||||
// Apply columns settings
|
||||
if (table->IsSettingsRequestLoad)
|
||||
TableLoadSettingsForColumns(table);
|
||||
if (table->IsInitializing || table->IsSettingsRequestLoad)
|
||||
{
|
||||
for (ImGuiTableColumn& column : table->Columns)
|
||||
{
|
||||
ImGuiTableFlags init_flags;
|
||||
if (table->IsSettingsRequestLoad)
|
||||
init_flags = column.IsLoadedSettings ? ~table->SettingsLoadedFlags : ~0;
|
||||
else
|
||||
init_flags = column.IsJustCreated ? ~0 : 0;
|
||||
TableInitColumnDefaults(table, &column, init_flags);
|
||||
}
|
||||
TableFixDisplayOrder(table); // Call even for non _Reorderable table as we loaded .ini data.
|
||||
table->IsSettingsRequestLoad = false;
|
||||
}
|
||||
|
||||
// Apply queued resizing/reordering/hiding requests
|
||||
TableApplyQueuedRequests(table);
|
||||
@@ -854,7 +889,6 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.
|
||||
// This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.
|
||||
const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ?
|
||||
const int columns_count = table->ColumnsCount;
|
||||
if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)
|
||||
{
|
||||
const float scale_factor = new_ref_scale_unit / table->RefScale;
|
||||
@@ -893,7 +927,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
{
|
||||
TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None);
|
||||
column->NameOffset = -1;
|
||||
column->UserData = 0;
|
||||
column->ID = column->UserData = 0;
|
||||
column->InitStretchWeightOrWidth = -1.0f;
|
||||
}
|
||||
|
||||
@@ -906,6 +940,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
table->IsSettingsDirty = true;
|
||||
}
|
||||
column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0;
|
||||
column->IsJustCreated = false;
|
||||
|
||||
if (column->IsEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1))
|
||||
table->IsDefaultVisibility = false;
|
||||
@@ -1645,9 +1680,9 @@ void ImGui::EndTable()
|
||||
NavUpdateCurrentWindowIsScrollPushableX();
|
||||
}
|
||||
|
||||
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
|
||||
// Called in TableUpdateLayout() when initializing/after loading settings.
|
||||
// 'init_mask' specify which fields to initialize.
|
||||
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
|
||||
void ImGui::TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
|
||||
{
|
||||
ImGuiTableColumnFlags flags = column->Flags;
|
||||
if (init_mask & ImGuiTableFlags_Resizable)
|
||||
@@ -1659,7 +1694,7 @@ static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column,
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
if (init_mask & ImGuiTableFlags_Reorderable)
|
||||
column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
|
||||
column->DisplayOrder = (ImGuiTableColumnIdx)((table->Flags & ImGuiTableFlags_Reorderable) ? -1 : table->Columns.index_from_ptr(column));
|
||||
if (init_mask & ImGuiTableFlags_Hideable)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
|
||||
if (init_mask & ImGuiTableFlags_Sortable)
|
||||
@@ -1696,20 +1731,10 @@ static void TableSetupColumnApply(ImGuiTable* table, int idx, ImGuiID id, ImS16
|
||||
column->ID = id;
|
||||
column->UserData = user_data;
|
||||
column->NameOffset = name_offset;
|
||||
flags = column->Flags;
|
||||
|
||||
// Initialize defaults
|
||||
column->InitStretchWeightOrWidth = init_width_or_weight;
|
||||
if (table->IsInitializing)
|
||||
{
|
||||
ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
|
||||
if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
|
||||
init_flags |= ImGuiTableFlags_Resizable;
|
||||
TableInitColumnDefaults(table, column, init_flags);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_data_for_sort_specs)
|
||||
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_data)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
@@ -1726,10 +1751,104 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
name_offset = (ImS16)table->ColumnsNames.size();
|
||||
table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
|
||||
}
|
||||
|
||||
const ImGuiID column_id = (label != NULL && label[0] != 0) ? ImHashStr(label) : 0;
|
||||
TableSetupColumnApply(table, table->DeclColumnsCount, column_id, name_offset, flags, init_width_or_weight, user_data_for_sort_specs);
|
||||
table->DeclColumnsCount++;
|
||||
|
||||
// When ID changed or a column moved: defer the request until layout where we will process full reconcile.
|
||||
const int column_idx = table->DeclColumnsCount++;
|
||||
ImGuiTableColumn* column = &table->Columns[column_idx];
|
||||
|
||||
// If topology change goes into reconcile mode
|
||||
if (table->IsReconcileMode == false && column->ID != column_id)
|
||||
{
|
||||
table->IsReconcileMode = true;
|
||||
table->TempData->ReconcileColumnsRequests.reserve(table->ColumnsCount - column_idx);
|
||||
}
|
||||
|
||||
// Fast/common path
|
||||
if (table->IsReconcileMode == false)
|
||||
{
|
||||
TableSetupColumnApply(table, column_idx, column_id, name_offset, flags, init_width_or_weight, user_data);
|
||||
column->IsNeedReconcileSrc = column->IsNeedReconcileDst = false;
|
||||
if (column_idx < table->TempData->OldColumnsData.size())
|
||||
table->TempData->OldColumnsData[column_idx].IsNeedReconcileSrc = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Reconcile path: defer applying data to TableUpdateLayout() -> TableReconcileMovedColumns() -> TableSetupColumnApply().
|
||||
// On column count change: search the previous columns:
|
||||
// - the live Columns[] array is truncated on shrink, so a moved column may only exist in old data.
|
||||
// - the live Columns[] array may already be partially reset.
|
||||
table->TempData->ReconcileColumnsRequests.push_back(ImGuiTableReconcileColumnData());
|
||||
ImGuiTableReconcileColumnData& reconcile_data = table->TempData->ReconcileColumnsRequests.back();
|
||||
reconcile_data.ID = column_id;
|
||||
reconcile_data.NameOffset = name_offset;
|
||||
reconcile_data.Flags = flags;
|
||||
reconcile_data.InitWidthOrWeight = init_width_or_weight;
|
||||
reconcile_data.UserData = user_data;
|
||||
reconcile_data.ColumnNewIdx = (ImGuiTableColumnIdx)column_idx;
|
||||
reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)-1;
|
||||
column->NameOffset = name_offset; // Allow TableGetColumnName() to work before layout
|
||||
column->IsNeedReconcileSrc = column->IsNeedReconcileDst = true;
|
||||
}
|
||||
|
||||
// NB: This was written to be similar to the logic in TableLoadSettingsForColumns().
|
||||
void ImGui::TableReconcileColumns(ImGuiTable* table)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTableTempData* temp_data = table->TempData;
|
||||
IM_UNUSED(g);
|
||||
IMGUI_DEBUG_LOG_TABLE("[table] Reconcile columns for table 0x%08X\n", table->ID);
|
||||
|
||||
ImSpan<ImGuiTableColumn>& dst_columns = table->Columns;
|
||||
ImSpan<ImGuiTableColumn>& src_columns = table->TempData->OldColumnsData.empty() ? table->Columns : table->TempData->OldColumnsData;
|
||||
|
||||
// Find matches for named columns.
|
||||
int matches = 0;
|
||||
ImVector<ImGuiTableReconcileColumnData>& reconcile_requests = temp_data->ReconcileColumnsRequests;
|
||||
for (ImGuiTableReconcileColumnData& reconcile_data : reconcile_requests)
|
||||
if (reconcile_data.ID != 0)
|
||||
for (ImGuiTableColumn& src_column : src_columns)
|
||||
if (src_column.ID == reconcile_data.ID && src_column.IsNeedReconcileSrc)
|
||||
{
|
||||
ImGuiTableColumn& dst_column = dst_columns[reconcile_data.ColumnNewIdx];
|
||||
IM_ASSERT(src_column.IsNeedReconcileSrc && dst_column.IsNeedReconcileDst);
|
||||
src_column.IsNeedReconcileSrc = dst_column.IsNeedReconcileDst = false;
|
||||
reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)src_columns.index_from_ptr(&src_column);
|
||||
reconcile_data.ColumnOldData = src_column;
|
||||
matches++;
|
||||
break;
|
||||
}
|
||||
|
||||
// Remaining entries are matched sequentially.
|
||||
int dst_idx = 0; // index in reconcile array
|
||||
if (matches != reconcile_requests.Size)
|
||||
for (ImGuiTableColumn& src_column : src_columns)
|
||||
{
|
||||
if (!src_column.IsNeedReconcileSrc)
|
||||
continue;
|
||||
while (dst_idx < reconcile_requests.Size && reconcile_requests[dst_idx].ColumnOldIdx != (ImGuiTableColumnIdx)-1)
|
||||
dst_idx++;
|
||||
if (dst_idx == reconcile_requests.Size)
|
||||
break;
|
||||
ImGuiTableReconcileColumnData& reconcile_data = reconcile_requests[dst_idx];
|
||||
ImGuiTableColumn& dst_column = dst_columns[reconcile_data.ColumnNewIdx];
|
||||
IM_ASSERT(src_column.IsNeedReconcileSrc && dst_column.IsNeedReconcileDst);
|
||||
dst_column.IsNeedReconcileSrc = dst_column.IsNeedReconcileDst = false;
|
||||
reconcile_data.ColumnOldIdx = (ImGuiTableColumnIdx)src_columns.index_from_ptr(&src_column);
|
||||
reconcile_data.ColumnOldData = src_column;
|
||||
}
|
||||
|
||||
// Apply in the final pass. Because it is possible that src_columns == table->Columns we went through a temporary copy.
|
||||
for (ImGuiTableReconcileColumnData& reconcile_data : reconcile_requests)
|
||||
{
|
||||
table->Columns[reconcile_data.ColumnNewIdx] = reconcile_data.ColumnOldData; // When old column was not found clear anyway with default-constructed data (which will set IsJustCreated=true)
|
||||
TableSetupColumnApply(table, reconcile_data.ColumnNewIdx, reconcile_data.ID, reconcile_data.NameOffset, reconcile_data.Flags, reconcile_data.InitWidthOrWeight, reconcile_data.UserData);
|
||||
IMGUI_DEBUG_LOG_TABLE("[table] - old %d -> new %d \"%s\"\n", reconcile_data.ColumnOldIdx, reconcile_data.ColumnNewIdx, TableGetColumnName(table, reconcile_data.ColumnNewIdx)); // Log at the end so NameOffset was copied.
|
||||
}
|
||||
TableFixDisplayOrder(table);
|
||||
table->IsSettingsDirty = true; // FIXME-RECONCILE: Necessary?
|
||||
table->IsReconcileMode = false;
|
||||
reconcile_requests.resize(0); // GC-ed once in NewFrame()
|
||||
}
|
||||
|
||||
// [Public]
|
||||
@@ -3721,6 +3840,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
|
||||
// - TableResetSettings()
|
||||
// - TableSaveSettings() [Internal]
|
||||
// - TableLoadSettings() [Internal]
|
||||
// - TableLoadSettingsForColumns() [Internal]
|
||||
// - TableSettingsHandler_ClearAll() [Internal]
|
||||
// - TableSettingsHandler_ApplyAll() [Internal]
|
||||
// - TableSettingsHandler_ReadOpen() [Internal]
|
||||
@@ -3755,6 +3875,8 @@ static size_t TableSettingsCalcChunkSize(int columns_count)
|
||||
ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
//ImGuiTableSettings* old_settings = TableSettingsFindByID(id); // Comment out sanity check to avoid unnecessary lookups.
|
||||
//IM_ASSERT(old_settings == NULL || old_settings->ColumnsCountMax < columns_count);
|
||||
ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count));
|
||||
TableSettingsInit(settings, id, columns_count, columns_count);
|
||||
return settings;
|
||||
@@ -3774,16 +3896,12 @@ ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id)
|
||||
// Get settings for a given table, NULL if none
|
||||
ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table)
|
||||
{
|
||||
if (table->SettingsOffset != -1)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);
|
||||
IM_ASSERT(settings->ID == table->ID);
|
||||
if (settings->ColumnsCountMax >= table->ColumnsCount)
|
||||
return settings; // OK
|
||||
settings->ID = 0; // Invalidate storage, we won't fit because of a count change
|
||||
}
|
||||
return NULL;
|
||||
if (table->SettingsOffset == -1)
|
||||
return NULL;
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);
|
||||
IM_ASSERT(settings->ID == table->ID);
|
||||
return settings;
|
||||
}
|
||||
|
||||
// Restore initial state of table (with or without saved settings)
|
||||
@@ -3804,6 +3922,11 @@ void ImGui::TableSaveSettings(ImGuiTable* table)
|
||||
// Bind or create settings data
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTableSettings* settings = TableGetBoundSettings(table);
|
||||
if (settings != NULL && table->ColumnsCount > settings->ColumnsCountMax)
|
||||
{
|
||||
settings->ID = 0; // Invalidate storage, we won't fit because of a count change
|
||||
settings = NULL;
|
||||
}
|
||||
if (settings == NULL)
|
||||
{
|
||||
settings = TableSettingsCreate(table->ID, table->ColumnsCount);
|
||||
@@ -3854,9 +3977,11 @@ void ImGui::TableSaveSettings(ImGuiTable* table)
|
||||
void ImGui::TableLoadSettings(ImGuiTable* table)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
table->IsSettingsRequestLoad = false;
|
||||
if (table->Flags & ImGuiTableFlags_NoSavedSettings)
|
||||
{
|
||||
table->IsSettingsRequestLoad = false; // Done
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind settings
|
||||
ImGuiTableSettings* settings;
|
||||
@@ -3876,44 +4001,83 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
|
||||
|
||||
table->SettingsLoadedFlags = settings->SaveFlags;
|
||||
table->RefScale = settings->RefScale;
|
||||
// TableUpdateLayout() will then call TableLoadSettingsForColumns() to apply the data.
|
||||
}
|
||||
|
||||
// Initialize default columns settings
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
TableInitColumnDefaults(table, column, ~0);
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
// NB: This was written to be similar to the logic in TableReconcileColumns().
|
||||
void ImGui::TableLoadSettingsForColumns(ImGuiTable* table)
|
||||
{
|
||||
for (ImGuiTableColumn& column : table->Columns)
|
||||
column.IsLoadedSettings = false;
|
||||
ImGuiTableSettings* settings = TableGetBoundSettings(table);
|
||||
if (settings == NULL)
|
||||
return;
|
||||
|
||||
table->SettingsLoadedFlags |= ImGuiTableFlags_Reorderable; // We handle above in code above.
|
||||
|
||||
// Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
|
||||
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
|
||||
for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++)
|
||||
|
||||
// Fast path
|
||||
int matches = 0;
|
||||
for (int n = 0; n < table->ColumnsCount; n++)
|
||||
{
|
||||
int column_n = column_settings->Index;
|
||||
if (column_n < 0 || column_n >= table->ColumnsCount)
|
||||
continue;
|
||||
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if (settings->SaveFlags & ImGuiTableFlags_Resizable)
|
||||
{
|
||||
if (column_settings->IsStretch)
|
||||
column->StretchWeight = column_settings->WidthOrWeight;
|
||||
else
|
||||
column->WidthRequest = column_settings->WidthOrWeight;
|
||||
}
|
||||
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
|
||||
column->DisplayOrder = column_settings->DisplayOrder;
|
||||
if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;
|
||||
column->SortOrder = column_settings->SortOrder;
|
||||
column->SortDirection = column_settings->SortDirection;
|
||||
if (n >= settings->ColumnsCount || column_settings[n].ID != table->Columns[n].ID)
|
||||
break;
|
||||
TableLoadSettingsForColumn(&table->Columns[n], &column_settings[n], settings->SaveFlags);
|
||||
matches++;
|
||||
}
|
||||
if (matches == settings->ColumnsCount)
|
||||
return;
|
||||
const int settings_start_n = matches; // Small optimization
|
||||
for (int n = settings_start_n; n < settings->ColumnsCount; n++)
|
||||
column_settings[n].IsLoaded = false;
|
||||
|
||||
// Fix display order and build index
|
||||
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
|
||||
TableFixDisplayOrder(table);
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n;
|
||||
// Find matches for named columns
|
||||
for (ImGuiTableColumn& column : table->Columns)
|
||||
if (column.ID != 0 && !column.IsLoadedSettings)
|
||||
for (int n = settings_start_n; n < settings->ColumnsCount; n++)
|
||||
if (column_settings[n].ID == column.ID && !column_settings[n].IsLoaded)
|
||||
{
|
||||
TableLoadSettingsForColumn(&column, &column_settings[n], settings->SaveFlags);
|
||||
matches++;
|
||||
break;
|
||||
}
|
||||
|
||||
// Remaining entries are matched sequentially
|
||||
int dst_idx = 0;
|
||||
for (int n = settings_start_n; n < settings->ColumnsCount; n++)
|
||||
if (!column_settings[n].IsLoaded)
|
||||
{
|
||||
while (dst_idx < table->ColumnsCount && table->Columns[dst_idx].IsLoadedSettings)
|
||||
dst_idx++;
|
||||
if (dst_idx >= table->ColumnsCount)
|
||||
break;
|
||||
TableLoadSettingsForColumn(&table->Columns[dst_idx], &column_settings[n], settings->SaveFlags);
|
||||
dst_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui::TableLoadSettingsForColumn(ImGuiTableColumn* column, ImGuiTableColumnSettings* column_settings, ImGuiTableFlags load_flags)
|
||||
{
|
||||
column->IsLoadedSettings = true;
|
||||
column_settings->IsLoaded = true;
|
||||
if (load_flags & ImGuiTableFlags_Resizable)
|
||||
{
|
||||
if (column_settings->IsStretch)
|
||||
column->StretchWeight = column_settings->WidthOrWeight;
|
||||
else
|
||||
column->WidthRequest = column_settings->WidthOrWeight;
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
if (load_flags & ImGuiTableFlags_Reorderable)
|
||||
column->DisplayOrder = column_settings->DisplayOrder;
|
||||
else
|
||||
column->DisplayOrder = column_settings->Index; // Because default depends on previous Index, we need to set that up and cannot rely on TableInitColumnDefaults()
|
||||
if ((load_flags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = (column_settings->IsEnabled == 1);
|
||||
column->SortOrder = column_settings->SortOrder;
|
||||
column->SortDirection = column_settings->SortDirection;
|
||||
}
|
||||
|
||||
struct ImGuiTableFixDisplayOrderColumnData
|
||||
@@ -3928,7 +4092,8 @@ static int IMGUI_CDECL TableFixDisplayOrderComparer(const void* lhs, const void*
|
||||
const ImGuiTable* table = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Table;
|
||||
const ImGuiTableColumnIdx lhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)lhs)->Idx;
|
||||
const ImGuiTableColumnIdx rhs_idx = ((const ImGuiTableFixDisplayOrderColumnData*)rhs)->Idx;
|
||||
const int order_delta = (table->Columns[lhs_idx].DisplayOrder - table->Columns[rhs_idx].DisplayOrder);
|
||||
// Assume ImGuiTableColumnIdx == signed short, to turn -1 into a large value so that it always sort after.
|
||||
const int order_delta = ((unsigned short)table->Columns[lhs_idx].DisplayOrder - (unsigned short)table->Columns[rhs_idx].DisplayOrder);
|
||||
return (order_delta > 0) ? +1 : (order_delta < 0) ? -1 : (lhs_idx > rhs_idx) ? +1 : -1;
|
||||
}
|
||||
|
||||
@@ -3946,6 +4111,8 @@ void ImGui::TableFixDisplayOrder(ImGuiTable* table)
|
||||
ImQsort(fdo_columns, (size_t)table->ColumnsCount, sizeof(ImGuiTableFixDisplayOrderColumnData), TableFixDisplayOrderComparer);
|
||||
for (int n = 0; n < table->ColumnsCount; n++)
|
||||
table->Columns[fdo_columns[n].Idx].DisplayOrder = (ImGuiTableColumnIdx)n;
|
||||
for (int n = 0; n < table->ColumnsCount; n++)
|
||||
table->DisplayOrderToIndex[table->Columns[n].DisplayOrder] = (ImGuiTableColumnIdx)n;
|
||||
}
|
||||
|
||||
static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
|
||||
@@ -4111,7 +4278,7 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data)
|
||||
temp_data->LastTimeActive = -1.0f;
|
||||
}
|
||||
|
||||
// Compact and remove unused settings data (currently only used by TestEngine)
|
||||
// Compact and remove unused or resize settings data (only TestEngine mark unused, but resizing table down can lead to compaction)
|
||||
void ImGui::TableGcCompactSettings()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
@@ -4127,6 +4294,9 @@ void ImGui::TableGcCompactSettings()
|
||||
if (settings->ID != 0)
|
||||
memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount));
|
||||
g.SettingsTables.swap(new_chunk_stream);
|
||||
for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
|
||||
if (ImGuiTable* table = g.Tables.TryGetMapData(table_n))
|
||||
table->SettingsOffset = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -4255,7 +4425,7 @@ void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings, ImGuiTable* tab
|
||||
#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||
|
||||
void ImGui::DebugNodeTable(ImGuiTable*) {}
|
||||
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*, ImGuiTable* table) {}
|
||||
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*, ImGuiTable*) {}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1584,7 +1584,7 @@ bool ImGui::TextLinkOpenURL(const char* label, const char* url)
|
||||
bool pressed = TextLink(label);
|
||||
if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL)
|
||||
g.PlatformIO.Platform_OpenInShellFn(&g, url);
|
||||
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label
|
||||
SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when it is the same as the label.
|
||||
if (BeginPopupContextItem())
|
||||
{
|
||||
if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink)))
|
||||
|
||||
Reference in New Issue
Block a user