diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0ad364a40..89b570fac 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -66,14 +66,33 @@ Autumn cleanup of stuff marked obsolete more than two years ago: Other Changes: -- Style: `StyleColorsDark()`: tweaked WindowBg to be darker than in 1.93.0. -- Style: added `style.SymbolArrowSizeFactor`, `style.SymbolArrowThicknessFactor` to tweak - the shape and look of arrows. Made default arrow stroked. (#5233, #2431, #2235, #6730, #8243) +- Style: + - `StyleColorsDark()`: tweaked WindowBg to be darker than in 1.93.0. + - Added `style.SymbolArrowSizeFactor`, `style.SymbolArrowThicknessFactor` to tweak the + shape and look of arrows. Made default arrow stroked. (#5233, #2431, #2235, #6730, #8243) Legacy arrow: size: (0.60f,0.70f), thickness: 0.0f New arrow: size: (0.72f,0.40f), thickness: 0.30f - TreeNode: - TreeNodeGetOpen(): added flags parameter to allow passing `ImGuiTreeNodeFlags_DefaultOpen` or `ImGuiTreeNodeFlags_Leaf`. (#9569, #9592, #3823, #9251) [@adalisk-emikhaylov, @Yaki-Blake] +- Popups: + - Modals: fixed an assert displaying a modal in an environment where + `io.DisplayFramebufferScale` is not an integer (regression in 1.93.0). (#9598) +- Tables: + - Fixed various border overshooting issues, some mostly visible with transparent + borders and under specific conditions: (#4678) + - On non-scrolling tables, inner horizontal borders slightly overlapped + outer vertical ones with certain settings. + - On non-scrolling tables, clamping lower border coordinates to stay within + visible range (no visible change, but it makes ImDrawCmd AABB more sensible). + - Fixed vertical inner borders slightly overlapping bottom outer border. + - Fixed various issues using `ImGuiTableFlags_NoHostExtendX`/`_NoHostExtendY`: + - Fixed selectable highlight straying off the X2/Y2 clipping limit. + - Fixed parent window work rectangle affecting the Y2 clipping limit. + - Fixed the Y2 clipping limit overlapping bottom outer border. +- Multi-Select: + - Fixed `ImGuiSelectionBasicStorage::GetNextSelectedItem()` reading one past the + end of storage when trailing items are unselected (#9604) [@MilosLord] - ImVec2: added a check for cases where a custom `IM_VEC2_CLASS_EXTRA` macro adds an extra `operator=` to ImVec2. This isn't supported but should not be needed as you should have a constructor to convert from your type to ImVec2. diff --git a/imgui.cpp b/imgui.cpp index 4645a1ba5..3a604ee54 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6334,7 +6334,8 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to strictly ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that) ImDrawCmd cmd = draw_list->CmdBuffer.back(); IM_ASSERT(cmd.ElemCount == 0); - draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); + draw_list->PrimReserve(6, 4); + draw_list->PrimRect(viewport_rect.Min, viewport_rect.Max, col); cmd = draw_list->CmdBuffer.back(); draw_list->CmdBuffer.pop_back(); draw_list->CmdBuffer.push_front(cmd); diff --git a/imgui.h b/imgui.h index 964e41502..5634b552f 100644 --- a/imgui.h +++ b/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.93.1 WIP" -#define IMGUI_VERSION_NUM 19301 +#define IMGUI_VERSION_NUM 19302 #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. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index d6cc255af..387de0b9d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6514,7 +6514,9 @@ static void DemoWindowTables() { static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells static bool init = true; - if (!show_widget_frame_bg) + if (show_widget_frame_bg) + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f); + else ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); for (int cell = 0; cell < 3 * 5; cell++) { @@ -6526,7 +6528,9 @@ static void DemoWindowTables() ImGui::InputText("##cell", text_bufs[cell], IM_COUNTOF(text_bufs[cell])); ImGui::PopID(); } - if (!show_widget_frame_bg) + if (show_widget_frame_bg) + ImGui::PopStyleVar(); + else ImGui::PopStyleColor(); init = false; ImGui::EndTable(); @@ -7040,6 +7044,7 @@ static void DemoWindowTables() if (ImGui::TreeNode("Outer size")) { IMGUI_DEMO_MARKER("Tables/Outer size"); + // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY // Important to that note how the two flags have slightly different behaviors! ImGui::Text("Using NoHostExtendX and NoHostExtendY:"); @@ -7135,7 +7140,7 @@ static void DemoWindowTables() // We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag. if (row_bg_type != 0) { - ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient? + ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.40f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.40f)); // Flat or Gradient? ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color); } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index aa4bc4966..79c493e76 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -7589,9 +7589,9 @@ void ImGui::RenderArrowEx(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float co // Pixel align when at a right angle if (is_fill && (cos_a * sin_a >= -FLT_EPSILON) && (cos_a * sin_a <= FLT_EPSILON)) { - p[0] = ImRoundSigned64(p[0]); - p[1] = ImRoundSigned64(p[1]); - p[2] = ImRoundSigned64(p[2]); + p[0] = ImRoundSigned(p[0]); + p[1] = ImRoundSigned(p[1]); + p[2] = ImRoundSigned(p[2]); } if (is_fill) diff --git a/imgui_internal.h b/imgui_internal.h index 5014eb6c8..3ddc5e410 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -295,8 +295,8 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 -#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // Positive values only! ImTrunc() is not inlined in MSVC debug builds -#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only! +#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // Positive values only! == ImTrunc() which is not inlined in MSVC debug builds +#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only! == ImRoundPositive() which is not inlined in MSVC debug builds. //#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023) // Hint for branch prediction @@ -553,14 +553,16 @@ inline float ImSaturate(float f) { return inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } -inline float ImTrunc(float f) { return (float)(int)(f); } +inline float ImTrunc(float f) { return (float)(int)(f); } // == IM_TRUNC() inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } +inline float ImTrunc64(float f) { return (float)(ImS64)(f); } inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } -inline float ImTrunc64(float f) { return (float)(ImS64)(f); } +inline float ImRoundPositive(float f) { return (float)(int)(f + 0.5f); } // == IM_ROUND(). Positive values only. inline float ImRoundPositive64(float f) { return (float)(ImS64)(f + 0.5f); } // Positive values only. +inline float ImRoundSigned(float f) { return (float)(int)(f >= 0.0f ? f + 0.5f : f - 0.5f); } +inline ImVec2 ImRoundSigned(const ImVec2& v) { return ImVec2((float)(int)(v.x >= 0.0f ? v.x + 0.5f : v.x - 0.5f), (float)(int)(v.y >= 0.0f ? v.y + 0.5f : v.y - 0.5f)); } inline float ImRoundSigned64(float f) { return (float)(ImS64)(f >= 0.0f ? f + 0.5f : f - 0.5f); } -inline ImVec2 ImRoundSigned64(const ImVec2& v) { return ImVec2((float)(ImS64)(v.x >= 0.0f ? v.x + 0.5f : v.x - 0.5f), (float)(ImS64)(v.y >= 0.0f ? v.y + 0.5f : v.y - 0.5f)); } inline float ImCeilFast(float f) { int i = (int)f; return (float)(i + (f > (float)i)); } // Consider using the bit-hack version (search for "0x1p120f"). inline int ImModPositive(int a, int b) { return (a + b) % b; } inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } @@ -664,6 +666,7 @@ struct IMGUI_API ImRect void TranslateY(float dy) { Min.y += dy; Max.y += dy; } void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. + void ShrinkClamp(const ImVec2& inset_min, const ImVec2& inset_max) { Min.x = ImMin(Min.x + inset_min.x, Max.x); Min.y = ImMin(Min.y + inset_min.y, Max.y); Max.x = ImMax(Max.x - inset_max.x, Min.x); Max.y = ImMax(Max.y - inset_max.y, Min.y); } bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } const ImVec4& AsVec4() const { return *(const ImVec4*)&Min.x; } @@ -3390,6 +3393,7 @@ struct IMGUI_API ImGuiTable ImU32 BorderColorLight; float BorderX1; float BorderX2; + float BorderSize; float HostIndentX; float MinColumnWidth; float OuterPaddingX; diff --git a/imgui_tables.cpp b/imgui_tables.cpp index e82c71dfb..483cf3004 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -478,29 +478,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); + // Border size + // FIXME-WIP: Changing this is not currently simple due to ClipRect requirements. Easier to manage with new 1.93 primitives though. + const float border_size = table->BorderSize = TABLE_BORDER_SIZE; + // Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752) - // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the - // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap - // problem only affect scrolling tables in this case we can get away with doing it without extra cost). - if (inner_window != outer_window) + // - We normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the + // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap + // problem only affect scrolling tables in this case we can get away with doing it without extra cost). + // - (FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize, + // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with + // different x/y values to BeginChild().) + // - For non-scrolling window, this is applied to table->InnerClipRect below. + ImRect border_size_outer = { - // FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize, - // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with - // different x/y values to BeginChild(). - const float border_size = TABLE_BORDER_SIZE; - if (flags & ImGuiTableFlags_BordersOuterV) - { - table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + border_size, table->HostClipRect.Max.x); - if (inner_window->DecoOuterSizeX2 == 0.0f) - table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - border_size, table->HostClipRect.Min.x); - } - if (flags & ImGuiTableFlags_BordersOuterH) - { - table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + border_size, table->HostClipRect.Max.y); - if (inner_window->DecoOuterSizeY2 == 0.0f) - table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - border_size, table->HostClipRect.Min.y); - } - } + (flags & ImGuiTableFlags_BordersOuterV) ? border_size : 0.0f, + (flags & ImGuiTableFlags_BordersOuterH) ? border_size : 0.0f, + ((flags & ImGuiTableFlags_BordersOuterV) && ((inner_window == outer_window) || inner_window->DecoOuterSizeX2 == 0.0f)) ? border_size : 0.0f, + ((flags & ImGuiTableFlags_BordersOuterH) && ((inner_window == outer_window) || inner_window->DecoOuterSizeY2 == 0.0f)) ? border_size : 0.0f, + }; + if (inner_window != outer_window) + table->HostClipRect.ShrinkClamp(border_size_outer.Min, border_size_outer.Max); // Padding and Spacing // - None ........Content..... Pad .....Content........ @@ -509,14 +507,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // - PadOuter+PadInner | Pad ..Content.. Pad | Pad ..Content.. Pad | const bool pad_outer_x = (flags & ImGuiTableFlags_NoPadOuterX) ? false : (flags & ImGuiTableFlags_PadOuterX) ? true : (flags & ImGuiTableFlags_BordersOuterV) != 0; const bool pad_inner_x = (flags & ImGuiTableFlags_NoPadInnerX) ? false : true; - const float inner_spacing_for_border = (flags & ImGuiTableFlags_BordersInnerV) ? TABLE_BORDER_SIZE : 0.0f; + const float inner_spacing_for_border = (flags & ImGuiTableFlags_BordersInnerV) ? border_size : 0.0f; const float inner_spacing_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) == 0) ? g.Style.CellPadding.x : 0.0f; const float inner_padding_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) != 0) ? g.Style.CellPadding.x : 0.0f; table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border; table->CellSpacingX2 = inner_spacing_explicit; table->CellPaddingX = inner_padding_explicit; - const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; + const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? border_size : 0.0f; const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f; table->OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table->CellPaddingX; @@ -525,9 +523,12 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->RowBgColorCounter = 0; table->LastRowFlags = ImGuiTableRowFlags_None; table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect; + if (inner_window == outer_window) + table->InnerClipRect.ShrinkClamp(border_size_outer.Min, border_size_outer.Max); table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width table->InnerClipRect.ClipWithFull(table->HostClipRect); - table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y; + if ((flags & ImGuiTableFlags_NoHostExtendY) == 0) + table->InnerClipRect.Max.y = table->HostClipRect.Max.y; table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() @@ -1277,7 +1278,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) } if (order_n < table->FreezeColumnsCount) - host_clip_rect.Min.x = ImClamp(column->MaxX + TABLE_BORDER_SIZE, host_clip_rect.Min.x, host_clip_rect.Max.x); + host_clip_rect.Min.x = ImClamp(column->MaxX + table->BorderSize, host_clip_rect.Min.x, host_clip_rect.Max.x); offset_x += column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f; } @@ -1309,8 +1310,9 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->Flags &= ~ImGuiTableFlags_NoHostExtendX; if (table->Flags & ImGuiTableFlags_NoHostExtendX) { - table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1; - table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1); + const float border_size_outer_w = (table->Flags & ImGuiTableFlags_BordersOuterV) ? table->BorderSize : 0.0f; + table->OuterRect.Max.x = table->WorkRect.Max.x = ImMin(table->OuterRect.Max.x, unused_x1 + border_size_outer_w); + table->InnerClipRect.Max.x = ImClamp(table->OuterRect.Max.x - border_size_outer_w, table->InnerClipRect.Min.x, table->InnerClipRect.Max.x); } table->InnerWindow->ParentWorkRect = table->WorkRect; table->BorderX1 = table->InnerClipRect.Min.x; @@ -1503,10 +1505,10 @@ void ImGui::EndTable() // but since the later is likely to be impossible to do we'd rather update both axes together. if (table->Flags & ImGuiTableFlags_ScrollX) { - const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; + const float border_size_outer_w = (table->Flags & ImGuiTableFlags_BordersOuterV) ? table->BorderSize : 0.0f; float max_pos_x = inner_window->DC.CursorMaxPos.x; if (table->RightMostEnabledColumn != -1) - max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border); + max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - border_size_outer_w); if (table->ResizedColumn != -1) max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); inner_window->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth; @@ -2168,7 +2170,6 @@ void ImGui::TableEndRow(ImGuiTable* table) // Decide of top border color ImU32 top_border_col = 0; - const float border_size = TABLE_BORDER_SIZE; if (table->CurrentRow > 0 && (table->Flags & ImGuiTableFlags_BordersInnerH)) top_border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight; @@ -2215,11 +2216,11 @@ void ImGui::TableEndRow(ImGuiTable* table) // Draw top border if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y) - window->DrawList->AddLineH(table->BorderX1, table->BorderX2, bg_y1, top_border_col, border_size); + window->DrawList->AddLineH(table->BorderX1, table->BorderX2, bg_y1, top_border_col, table->BorderSize, ImDrawFlags_StrokeCenterBiased); // Draw bottom border at the row unfreezing mark (always strong) if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y) - window->DrawList->AddLineH(table->BorderX1, table->BorderX2, bg_y2, table->BorderColorStrong, border_size); + window->DrawList->AddLineH(table->BorderX1, table->BorderX2, bg_y2, table->BorderColorStrong, table->BorderSize, ImDrawFlags_StrokeCenterBiased); } // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle) @@ -2734,6 +2735,10 @@ void ImGui::TableSetupDrawChannels(ImGuiTable* table) table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect; table->Bg2ClipRectForDrawCmd = table->HostClipRect; IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y); + if (table->Flags & ImGuiTableFlags_NoHostExtendX) + table->Bg2ClipRectForDrawCmd.Max.x = ImMin(table->Bg2ClipRectForDrawCmd.Max.x, table->BgClipRect.Max.x); // Don't apply to Bg0: shapes in Bg0 are already CPU clipped and it makes merging fails. + if (table->Flags & ImGuiTableFlags_NoHostExtendY) + table->Bg2ClipRectForDrawCmd.Max.y = ImMin(table->Bg2ClipRectForDrawCmd.Max.y, table->BgClipRect.Max.y); // " } // This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable(). @@ -2891,7 +2896,7 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x); if ((merge_group_n & 2) == 0 || !has_freeze_v) merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y); - if ((merge_group_n & 1) != 0) + if ((merge_group_n & 1) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendX) == 0) merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x); if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0) merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y); @@ -2951,19 +2956,25 @@ static ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_ void ImGui::TableDrawBorders(ImGuiTable* table) { ImGuiWindow* inner_window = table->InnerWindow; - if (!table->OuterWindow->ClipRect.Overlaps(table->OuterRect)) + if (!table->OuterWindow->ClipRect.Overlaps(table->OuterRect)) // Note how OuterWindow->ClipRect == table->Bg0ClipRectForDrawCmd return; ImDrawList* inner_drawlist = inner_window->DrawList; table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0); - inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false); + inner_drawlist->PushClipRect(table->OuterWindow->ClipRect.Min, table->OuterWindow->ClipRect.Max, false); + + // For e.g. non scrolling table we limit vertex coordinates in order to keep ImDrawCmd AABB roughly within visible range. + const float border_size_outer_w = (table->Flags & ImGuiTableFlags_BordersOuterV) ? table->BorderSize : 0.0f; + const float border_size_outer_h = (table->Flags & ImGuiTableFlags_BordersOuterH) ? table->BorderSize : 0.0f; + const float border_size_inner = table->BorderSize; + const float draw_y2_limit = table->OuterWindow->ClipRect.Max.y + (2.0f * ImMax(border_size_outer_h, inner_drawlist->_FringeScale)); // Draw inner border and resizing feedback ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); - const float border_size = TABLE_BORDER_SIZE; - const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? border_size : 0.0f); - const float draw_y2_body = table->InnerRect.Max.y; - const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1; + const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? border_size_outer_h : 0.0f); + const float draw_y2_body = ImMin(table->InnerRect.Max.y - border_size_outer_h, draw_y2_limit); + const float draw_y2_head = table->IsUsingHeaders ? ImMin(draw_y2_body, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1; + if (table->Flags & ImGuiTableFlags_BordersInnerV) { for (int order_n = 0; order_n < table->ColumnsCount; order_n++) @@ -2996,7 +3007,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0) draw_y2 = draw_y2_body; if (draw_y2 > draw_y1) - inner_drawlist->AddLineV(column->MaxX, draw_y1, draw_y2, TableGetColumnBorderCol(table, order_n, column_n), border_size); + inner_drawlist->AddLineV(column->MaxX, draw_y1, draw_y2, TableGetColumnBorderCol(table, order_n, column_n), border_size_inner); } } @@ -3009,30 +3020,30 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part // of it in inner window, and the part that's over scrollbars in the outer window..) // Either solution currently won't allow us to use a larger border size: the border would clipped. - const ImRect outer_border = table->OuterRect; + ImRect outer_border = table->OuterRect; + outer_border.Max.y = ImMin(outer_border.Max.y, draw_y2_limit); // Clamp but keep bottom border non-visible. const ImU32 outer_col = table->BorderColorStrong; if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) { - inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, border_size); + inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, border_size_outer_w); } else if (table->Flags & ImGuiTableFlags_BordersOuterV) { - inner_drawlist->AddLineV(outer_border.Min.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size); - inner_drawlist->AddLineV(outer_border.Max.x - border_size, outer_border.Min.y, outer_border.Max.y, outer_col, border_size); + inner_drawlist->AddLineV(outer_border.Min.x, outer_border.Min.y, outer_border.Max.y, outer_col, border_size_outer_w); + inner_drawlist->AddLineV(outer_border.Max.x - border_size_outer_w, outer_border.Min.y, outer_border.Max.y, outer_col, border_size_outer_w); } else if (table->Flags & ImGuiTableFlags_BordersOuterH) { - inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size); - inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y - border_size, outer_col, border_size); + inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size_outer_h); + inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y - border_size_outer_h, outer_col, border_size_outer_h); } } if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) { - // Draw bottom-most row border between it is above outer border. - const float h_inset = (table->Flags & ImGuiTableFlags_BordersOuterV) ? border_size : 0.0f; + // Draw bottom-most row border between it and above outer border. const float border_y = table->RowPosY2; if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y) - inner_drawlist->AddLineH(table->BorderX1 + h_inset, table->BorderX2 - h_inset, border_y, table->BorderColorLight, border_size); + inner_drawlist->AddLineH(table->BorderX1, table->BorderX2, border_y, table->BorderColorLight, border_size_inner); } inner_drawlist->PopClipRect(); @@ -3625,8 +3636,9 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label if (pass == 1) { // Draw border - const float border_size = TABLE_BORDER_SIZE; - draw_list->AddLine(bg_shape[0] + ImVec2(border_size * 0.5f, 0.0f), bg_shape[3] + ImVec2(border_size * 0.5f, 0.0f), TableGetColumnBorderCol(table, order_n, column_n), border_size); + draw_list->AddLine( + bg_shape[0] + ImVec2(table->BorderSize * 0.5f, 0.0f), + bg_shape[3] + ImVec2(table->BorderSize * 0.5f, 0.0f), TableGetColumnBorderCol(table, order_n, column_n), table->BorderSize); } } PopClipRect(); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6e3e494a9..1131bad81 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4478,7 +4478,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons if (new_text_len + BufTextLen + 1 > obj->TextA.Size && is_resizable && !is_readonly) { IM_ASSERT(Buf == obj->TextA.Data); - int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1; + int new_buf_size = BufTextLen + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1; obj->TextA.resize(new_buf_size + 1); obj->TextSrc = obj->TextA.Data; Buf = obj->TextA.Data; @@ -8810,7 +8810,7 @@ bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* it = _Storage.Data.Data; IM_ASSERT(it >= _Storage.Data.Data && it <= it_end); if (it != it_end) - while (it->val_i == 0 && it < it_end) + while (it < it_end && it->val_i == 0) it++; const bool has_more = (it != it_end); *opaque_it = has_more ? (void**)(it + 1) : (void**)(it);