From b929de520684ae830b24f6a5b0422aae73ed21ab Mon Sep 17 00:00:00 2001 From: 4DBug <4DBug@github.com> Date: Thu, 15 Oct 2026 02:05:43 -0500 Subject: [PATCH] Fix: ImDrawList:PushClipRectFullScreen() was missing (drop target outside the window clip rect crashed); CalcItemSize() no longer writes to the caller's size (by value like upstream; fixes BeginTable UserOuterSize), scalar _CalcItemSizeXY for internal callers; t_fixes covers both --- build/ImGui.linemap.json | 2 +- build/ImGui.luau | 39 +++++++++++++++++++++------------ build/ImGuiTyped.luau | 1 + build/imgui_love.linemap.json | 2 +- build/imgui_love.lua | 39 +++++++++++++++++++++------------ imgui.lua | 26 +++++++++++++--------- imgui_draw.lua | 5 +++++ imgui_widgets.lua | 8 +++---- tools/test/scripts/t_fixes.luau | 9 +++++++- 9 files changed, 86 insertions(+), 45 deletions(-) diff --git a/build/ImGui.linemap.json b/build/ImGui.linemap.json index 709a77b..9040d12 100644 --- a/build/ImGui.linemap.json +++ b/build/ImGui.linemap.json @@ -1 +1 @@ -[[46, "imgui.lua"], [21389, "imgui_demo.lua"], [29807, "imgui_draw.lua"], [35745, "imgui_h.lua"], [38412, "imgui_impl_roblox.lua"], [40060, "imgui_impl_roblox_fonts.lua"], [46463, "imgui_internal.lua"], [49932, "imgui_tables.lua"], [54991, "imgui_widgets.lua"], [65631, "imstb_rectpack.lua"], [66295, "imstb_textedit.lua"], [67837, "imstb_truetype.lua"], [70625, "imstd_minstdio.lua"]] \ No newline at end of file +[[46, "imgui.lua"], [21395, "imgui_demo.lua"], [29813, "imgui_draw.lua"], [35756, "imgui_h.lua"], [38423, "imgui_impl_roblox.lua"], [40071, "imgui_impl_roblox_fonts.lua"], [46474, "imgui_internal.lua"], [49943, "imgui_tables.lua"], [55002, "imgui_widgets.lua"], [65642, "imstb_rectpack.lua"], [66306, "imstb_textedit.lua"], [67848, "imstb_truetype.lua"], [70636, "imstd_minstdio.lua"]] \ No newline at end of file diff --git a/build/ImGui.luau b/build/ImGui.luau index b26877b..64a65e3 100644 --- a/build/ImGui.luau +++ b/build/ImGui.luau @@ -6986,8 +6986,14 @@ end --- @param default_h float --- @return ImVec2 function ImGui.CalcItemSize(size, default_w, default_h) + return ImVec2(ImGui._CalcItemSizeXY(size.x, size.y, default_w, default_h)) -- [Luau] 'size' is by value upstream: never written to +end + +-- [Luau] scalar CalcItemSize(): callers that build 'size' themselves skip both ImVec2 allocations +--- @return float, float +function ImGui._CalcItemSizeXY(size_x, size_y, default_w, default_h) local avail_x, avail_y - if size.x < 0.0 or size.y < 0.0 then + if size_x < 0.0 or size_y < 0.0 then -- [Luau] scalar GetContentRegionAvail(), no temporary ImVec2 local g = GImGui local window = g.CurrentWindow @@ -6995,19 +7001,19 @@ function ImGui.CalcItemSize(size, default_w, default_h) avail_x = mx.x - window.DC.CursorPos.x; avail_y = mx.y - window.DC.CursorPos.y end - if size.x == 0.0 then - size.x = default_w - elseif size.x < 0.0 then - size.x = math.max(4.0, avail_x + size.x) -- <-- size.x is negative here so we are subtracting + if size_x == 0.0 then + size_x = default_w + elseif size_x < 0.0 then + size_x = math.max(4.0, avail_x + size_x) -- <-- size.x is negative here so we are subtracting end - if size.y == 0.0 then - size.y = default_h - elseif size.y < 0.0 then - size.y = math.max(4.0, avail_y + size.y) -- <-- size.y is negative here so we are subtracting + if size_y == 0.0 then + size_y = default_h + elseif size_y < 0.0 then + size_y = math.max(4.0, avail_y + size_y) -- <-- size.y is negative here so we are subtracting end - return size + return size_x, size_y end function ImGui.GetTextLineHeight() @@ -34804,6 +34810,11 @@ function MT.ImDrawList:PushClipRect(cr_min, cr_max, intersect_with_current_clip_ self:_OnChangedClipRect() end +function MT.ImDrawList:PushClipRectFullScreen() + local cr = self._Data.ClipRectFullscreen + self:PushClipRect(ImVec2(cr.x, cr.y), ImVec2(cr.z, cr.w)) +end + function MT.ImDrawList:PopClipRect() local stack = self._ClipRectStack assert(stack.Size > 0) @@ -55064,6 +55075,7 @@ local IM_U32_MAX = UINT_MAX -- (0xFFFFFFFF) --- @param text_end? int --- @param flags? ImGuiTextFlags local TextEx_bb = ImRect() -- [Luau] scratch, never escapes TextEx() +local LAYOUT_ZERO_SIZE = ImVec2(0, 0) -- [Luau] shared read-only: ItemSize()/CalcItemSize() only read it @native function ImGui.TextEx(text, text_end, flags) @@ -55264,7 +55276,7 @@ function ImGui.TextAligned(align_x, size_x, fmt, ...) local text = ImFormatString(fmt, ...) local text_end = #text + 1 local text_size = ImGui.CalcTextSize(text, text_end) - size_x = ImGui.CalcItemSize(ImVec2(size_x, 0.0), 0.0, text_size.y).x + size_x = ImGui._CalcItemSizeXY(size_x, 0.0, 0.0, text_size.y) local pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset) local pos_max = ImVec2(pos.x + size_x, window.ClipRect.Max.y) @@ -55732,7 +55744,7 @@ local ButtonEx_text_min, ButtonEx_text_max = ImVec2(), ImVec2() @native function ImGui.ButtonEx(label, size_arg, flags) - if size_arg == nil then size_arg = ImVec2(0, 0) end + if size_arg == nil then size_arg = LAYOUT_ZERO_SIZE end -- [Luau] shared read-only: CalcItemSize() only reads it if flags == nil then flags = 0 end local window = ImGui.GetCurrentWindow() @@ -55823,7 +55835,7 @@ function ImGui.InvisibleButton(str_id, size_arg, flags) end -- Ensure zero-size fits to contents - local size = ImGui.CalcItemSize(ImVec2(size_arg.x ~= 0.0 and size_arg.x or -1.1754943508223e-38 --[[FLT_MIN]], size_arg.y ~= 0.0 and size_arg.y or -1.1754943508223e-38 --[[FLT_MIN]]), 0.0, 0.0) + local size = ImVec2(ImGui._CalcItemSizeXY(size_arg.x ~= 0.0 and size_arg.x or -1.1754943508223e-38 --[[FLT_MIN]], size_arg.y ~= 0.0 and size_arg.y or -1.1754943508223e-38 --[[FLT_MIN]], 0.0, 0.0)) local id = window:GetID(str_id) local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + size) @@ -56686,7 +56698,6 @@ end -- - Separator() ------------------------------------------------------------------------------- -local LAYOUT_ZERO_SIZE = ImVec2(0, 0) -- [Luau] shared read-only: ItemSize() only reads it local LayoutHelpers_size = ImVec2() -- [Luau] scratch for ItemSize() arguments function ImGui.Spacing() diff --git a/build/ImGuiTyped.luau b/build/ImGuiTyped.luau index 8f85466..19875ec 100644 --- a/build/ImGuiTyped.luau +++ b/build/ImGuiTyped.luau @@ -886,6 +886,7 @@ export type ImGuiAPI = { WindowSettingsAddSettingsHandler: (...any) -> ...any, WindowSyncOwnedViewport: (...any) -> ...any, _ByteArrayToString: (...any) -> ...any, + _CalcItemSizeXY: (...any) -> ...any, _GetTypeFunc: (...any) -> ...any, _SetCurrentContext_Internal: (...any) -> ...any, _SetCurrentContext_Tables: (...any) -> ...any, diff --git a/build/imgui_love.linemap.json b/build/imgui_love.linemap.json index 9e37dd8..b7d599c 100644 --- a/build/imgui_love.linemap.json +++ b/build/imgui_love.linemap.json @@ -1 +1 @@ -[[183, "imgui.lua"], [21526, "imgui_demo.lua"], [29944, "imgui_draw.lua"], [35882, "imgui_h.lua"], [38549, "imgui_impl_love.lua"], [39089, "imgui_internal.lua"], [42558, "imgui_tables.lua"], [47617, "imgui_widgets.lua"], [58257, "imstb_rectpack.lua"], [58921, "imstb_textedit.lua"], [60463, "imstb_truetype.lua"], [63251, "imstd_minstdio.lua"]] \ No newline at end of file +[[183, "imgui.lua"], [21532, "imgui_demo.lua"], [29950, "imgui_draw.lua"], [35893, "imgui_h.lua"], [38560, "imgui_impl_love.lua"], [39100, "imgui_internal.lua"], [42569, "imgui_tables.lua"], [47628, "imgui_widgets.lua"], [58268, "imstb_rectpack.lua"], [58932, "imstb_textedit.lua"], [60474, "imstb_truetype.lua"], [63262, "imstd_minstdio.lua"]] \ No newline at end of file diff --git a/build/imgui_love.lua b/build/imgui_love.lua index aa7cdeb..ec5b02b 100644 --- a/build/imgui_love.lua +++ b/build/imgui_love.lua @@ -7123,8 +7123,14 @@ end --- @param default_h float --- @return ImVec2 function ImGui.CalcItemSize(size, default_w, default_h) + return ImVec2(ImGui._CalcItemSizeXY(size.x, size.y, default_w, default_h)) -- [Luau] 'size' is by value upstream: never written to +end + +-- [Luau] scalar CalcItemSize(): callers that build 'size' themselves skip both ImVec2 allocations +--- @return float, float +function ImGui._CalcItemSizeXY(size_x, size_y, default_w, default_h) local avail_x, avail_y - if size.x < 0.0 or size.y < 0.0 then + if size_x < 0.0 or size_y < 0.0 then -- [Luau] scalar GetContentRegionAvail(), no temporary ImVec2 local g = GImGui local window = g.CurrentWindow @@ -7132,19 +7138,19 @@ function ImGui.CalcItemSize(size, default_w, default_h) avail_x = mx.x - window.DC.CursorPos.x; avail_y = mx.y - window.DC.CursorPos.y end - if size.x == 0.0 then - size.x = default_w - elseif size.x < 0.0 then - size.x = math.max(4.0, avail_x + size.x) -- <-- size.x is negative here so we are subtracting + if size_x == 0.0 then + size_x = default_w + elseif size_x < 0.0 then + size_x = math.max(4.0, avail_x + size_x) -- <-- size.x is negative here so we are subtracting end - if size.y == 0.0 then - size.y = default_h - elseif size.y < 0.0 then - size.y = math.max(4.0, avail_y + size.y) -- <-- size.y is negative here so we are subtracting + if size_y == 0.0 then + size_y = default_h + elseif size_y < 0.0 then + size_y = math.max(4.0, avail_y + size_y) -- <-- size.y is negative here so we are subtracting end - return size + return size_x, size_y end function ImGui.GetTextLineHeight() @@ -34941,6 +34947,11 @@ function MT.ImDrawList:PushClipRect(cr_min, cr_max, intersect_with_current_clip_ self:_OnChangedClipRect() end +function MT.ImDrawList:PushClipRectFullScreen() + local cr = self._Data.ClipRectFullscreen + self:PushClipRect(ImVec2(cr.x, cr.y), ImVec2(cr.z, cr.w)) +end + function MT.ImDrawList:PopClipRect() local stack = self._ClipRectStack assert(stack.Size > 0) @@ -47690,6 +47701,7 @@ local IM_U32_MAX = UINT_MAX -- (0xFFFFFFFF) --- @param text_end? int --- @param flags? ImGuiTextFlags local TextEx_bb = ImRect() -- [Luau] scratch, never escapes TextEx() +local LAYOUT_ZERO_SIZE = ImVec2(0, 0) -- [Luau] shared read-only: ItemSize()/CalcItemSize() only read it function ImGui.TextEx(text, text_end, flags) @@ -47890,7 +47902,7 @@ function ImGui.TextAligned(align_x, size_x, fmt, ...) local text = ImFormatString(fmt, ...) local text_end = #text + 1 local text_size = ImGui.CalcTextSize(text, text_end) - size_x = ImGui.CalcItemSize(ImVec2(size_x, 0.0), 0.0, text_size.y).x + size_x = ImGui._CalcItemSizeXY(size_x, 0.0, 0.0, text_size.y) local pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset) local pos_max = ImVec2(pos.x + size_x, window.ClipRect.Max.y) @@ -48358,7 +48370,7 @@ local ButtonEx_text_min, ButtonEx_text_max = ImVec2(), ImVec2() function ImGui.ButtonEx(label, size_arg, flags) - if size_arg == nil then size_arg = ImVec2(0, 0) end + if size_arg == nil then size_arg = LAYOUT_ZERO_SIZE end -- [Luau] shared read-only: CalcItemSize() only reads it if flags == nil then flags = 0 end local window = ImGui.GetCurrentWindow() @@ -48449,7 +48461,7 @@ function ImGui.InvisibleButton(str_id, size_arg, flags) end -- Ensure zero-size fits to contents - local size = ImGui.CalcItemSize(ImVec2(size_arg.x ~= 0.0 and size_arg.x or -1.1754943508223e-38 --[[FLT_MIN]], size_arg.y ~= 0.0 and size_arg.y or -1.1754943508223e-38 --[[FLT_MIN]]), 0.0, 0.0) + local size = ImVec2(ImGui._CalcItemSizeXY(size_arg.x ~= 0.0 and size_arg.x or -1.1754943508223e-38 --[[FLT_MIN]], size_arg.y ~= 0.0 and size_arg.y or -1.1754943508223e-38 --[[FLT_MIN]], 0.0, 0.0)) local id = window:GetID(str_id) local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + size) @@ -49312,7 +49324,6 @@ end -- - Separator() ------------------------------------------------------------------------------- -local LAYOUT_ZERO_SIZE = ImVec2(0, 0) -- [Luau] shared read-only: ItemSize() only reads it local LayoutHelpers_size = ImVec2() -- [Luau] scratch for ItemSize() arguments function ImGui.Spacing() diff --git a/imgui.lua b/imgui.lua index d87101d..0a3fff1 100644 --- a/imgui.lua +++ b/imgui.lua @@ -6941,8 +6941,14 @@ end --- @param default_h float --- @return ImVec2 function ImGui.CalcItemSize(size, default_w, default_h) + return ImVec2(ImGui._CalcItemSizeXY(size.x, size.y, default_w, default_h)) -- [Luau] 'size' is by value upstream: never written to +end + +-- [Luau] scalar CalcItemSize(): callers that build 'size' themselves skip both ImVec2 allocations +--- @return float, float +function ImGui._CalcItemSizeXY(size_x, size_y, default_w, default_h) local avail_x, avail_y - if size.x < 0.0 or size.y < 0.0 then + if size_x < 0.0 or size_y < 0.0 then -- [Luau] scalar GetContentRegionAvail(), no temporary ImVec2 local g = GImGui local window = g.CurrentWindow @@ -6950,19 +6956,19 @@ function ImGui.CalcItemSize(size, default_w, default_h) avail_x = mx.x - window.DC.CursorPos.x; avail_y = mx.y - window.DC.CursorPos.y end - if size.x == 0.0 then - size.x = default_w - elseif size.x < 0.0 then - size.x = math.max(4.0, avail_x + size.x) -- <-- size.x is negative here so we are subtracting + if size_x == 0.0 then + size_x = default_w + elseif size_x < 0.0 then + size_x = math.max(4.0, avail_x + size_x) -- <-- size.x is negative here so we are subtracting end - if size.y == 0.0 then - size.y = default_h - elseif size.y < 0.0 then - size.y = math.max(4.0, avail_y + size.y) -- <-- size.y is negative here so we are subtracting + if size_y == 0.0 then + size_y = default_h + elseif size_y < 0.0 then + size_y = math.max(4.0, avail_y + size_y) -- <-- size.y is negative here so we are subtracting end - return size + return size_x, size_y end function ImGui.GetTextLineHeight() diff --git a/imgui_draw.lua b/imgui_draw.lua index c92b310..a89f85b 100644 --- a/imgui_draw.lua +++ b/imgui_draw.lua @@ -4998,6 +4998,11 @@ function MT.ImDrawList:PushClipRect(cr_min, cr_max, intersect_with_current_clip_ self:_OnChangedClipRect() end +function MT.ImDrawList:PushClipRectFullScreen() + local cr = self._Data.ClipRectFullscreen + self:PushClipRect(ImVec2(cr.x, cr.y), ImVec2(cr.z, cr.w)) +end + function MT.ImDrawList:PopClipRect() local stack = self._ClipRectStack IM_ASSERT(stack.Size > 0) diff --git a/imgui_widgets.lua b/imgui_widgets.lua index 6b873a9..9a046b6 100644 --- a/imgui_widgets.lua +++ b/imgui_widgets.lua @@ -74,6 +74,7 @@ local IM_U32_MAX = UINT_MAX -- (0xFFFFFFFF) --- @param text_end? int --- @param flags? ImGuiTextFlags local TextEx_bb = ImRect() -- [Luau] scratch, never escapes TextEx() +local LAYOUT_ZERO_SIZE = ImVec2(0, 0) -- [Luau] shared read-only: ItemSize()/CalcItemSize() only read it @native function ImGui.TextEx(text, text_end, flags) @@ -274,7 +275,7 @@ function ImGui.TextAligned(align_x, size_x, fmt, ...) local text = ImFormatString(fmt, ...) local text_end = #text + 1 local text_size = ImGui.CalcTextSize(text, text_end) - size_x = ImGui.CalcItemSize(ImVec2(size_x, 0.0), 0.0, text_size.y).x + size_x = ImGui._CalcItemSizeXY(size_x, 0.0, 0.0, text_size.y) local pos = ImVec2(window.DC.CursorPos.x, window.DC.CursorPos.y + window.DC.CurrLineTextBaseOffset) local pos_max = ImVec2(pos.x + size_x, window.ClipRect.Max.y) @@ -742,7 +743,7 @@ local ButtonEx_text_min, ButtonEx_text_max = ImVec2(), ImVec2() @native function ImGui.ButtonEx(label, size_arg, flags) - if size_arg == nil then size_arg = ImVec2(0, 0) end + if size_arg == nil then size_arg = LAYOUT_ZERO_SIZE end -- [Luau] shared read-only: CalcItemSize() only reads it if flags == nil then flags = 0 end local window = ImGui.GetCurrentWindow() @@ -833,7 +834,7 @@ function ImGui.InvisibleButton(str_id, size_arg, flags) end -- Ensure zero-size fits to contents - local size = ImGui.CalcItemSize(ImVec2(size_arg.x ~= 0.0 and size_arg.x or -FLT_MIN, size_arg.y ~= 0.0 and size_arg.y or -FLT_MIN), 0.0, 0.0) + local size = ImVec2(ImGui._CalcItemSizeXY(size_arg.x ~= 0.0 and size_arg.x or -FLT_MIN, size_arg.y ~= 0.0 and size_arg.y or -FLT_MIN, 0.0, 0.0)) local id = window:GetID(str_id) local bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + size) @@ -1696,7 +1697,6 @@ end -- - Separator() ------------------------------------------------------------------------------- -local LAYOUT_ZERO_SIZE = ImVec2(0, 0) -- [Luau] shared read-only: ItemSize() only reads it local LayoutHelpers_size = ImVec2() -- [Luau] scratch for ItemSize() arguments function ImGui.Spacing() diff --git a/tools/test/scripts/t_fixes.luau b/tools/test/scripts/t_fixes.luau index 7f4bb77..c90fa90 100644 --- a/tools/test/scripts/t_fixes.luau +++ b/tools/test/scripts/t_fixes.luau @@ -1,4 +1,5 @@ --- Regressions vs upstream: clipboard paste filter (InputTextEx), ImParseFormatTrimDecorations (Ctrl+click temp input), ShrinkWidths tie order +-- Regressions vs upstream: clipboard paste filter (InputTextEx), ImParseFormatTrimDecorations (Ctrl+click temp input), ShrinkWidths tie order, +-- CalcItemSize by value, PushClipRectFullScreen (drop target outside clip rect) local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui")) local ImGui, ImVec2 = G.ImGui, G.ImVec2 ImGui.CreateContext(); G.ImGui_ImplRoblox.Init() @@ -48,6 +49,12 @@ game:GetService("RunService").Stepped:Connect(function() ImGui.InputText("##t", buf, 64); rects.t = rect() sf = ImGui.SliderFloat("##s", sf, 0.0, 100.0, "%.0f deg"); rects.s = rect() di = ImGui.DragInt("##d", di, 1, 0, 100, "%d%%"); rects.d = rect() + -- CalcItemSize takes 'size' by value: the caller's ImVec2 must not be written to + local bsz, csz = ImVec2(0, 0), ImVec2(-1, 40) + ImGui.Button("sz", bsz); check(bsz.x == 0 and bsz.y == 0, "Button wrote size_arg") + ImGui.BeginChild("c", csz); ImGui.EndChild(); check(csz.x == -1 and csz.y == 40, "BeginChild wrote size_arg") + -- drop target rect sticking out of the window clip rect pushes a full-screen clip rect + ImGui.RenderDragDropTargetRectForItem(G.ImRect(-50, -50, 900, 900)) ImGui.End() ImGui.Render(); G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData()) if f == 8 then check(edit_text() == "abc\u{e9}", "paste: '" .. edit_text() .. "'") end