From e575a5fda6cc0f86d61b1b5b1a1151d6d6630492 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:04:51 +0800 Subject: [PATCH 01/22] Implement `SliderBehaviorT()` and `SliderBehavior()` stub Add some datatype casting helpers --- lua/imgui_h.lua | 29 ++++-- lua/imgui_widgets.lua | 206 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 6 deletions(-) diff --git a/lua/imgui_h.lua b/lua/imgui_h.lua index fab07ef..33792e1 100644 --- a/lua/imgui_h.lua +++ b/lua/imgui_h.lua @@ -14,13 +14,30 @@ local _band = bit.band --- @class ImU16 : integer --- @class ImS16 : integer -function ImU8(val) return _band(val, 0xFF) end --- @type fun(val: number): ImU8 -function ImS8(val) return _band(val, 0xFF) - (_band(val, 0x80) ~= 0 and 0x100 or 0) end --- @type fun(val: number): ImS8 +--- @class ImU32 : integer +--- @class ImS32 : integer + +--- @param val number +--- @return ImU8 +function ImU8(val) return _band(val, 0xFF) end +--- @param val number +--- @return ImS8 +function ImS8(val) return _band(val, 0xFF) - (_band(val, 0x80) ~= 0 and 0x100 or 0) end + +--- @param val number +--- @return ImU16 +function ImU16(val) return _band(val, 0xFFFF) end +--- @param val number +--- @return ImS16 +function ImS16(val) return _band(val, 0xFFFF) - (_band(val, 0x8000) ~= 0 and 0x10000 or 0) end + +--- @param val number +--- @return ImU32 +function ImU32(val) return _band(val, 0xFFFFFFFF) end +--- @param val number +--- @return ImS32 +function ImS32(val) return _band(val, 0xFFFFFFFF) - (_band(val, 0x80000000) ~= 0 and 0x100000000 or 0) end -function ImU16(val) return _band(val, 0xFFFF) end --- @type fun(val: number): ImU16 -function ImS16(val) return _band(val, 0xFFFF) - (_band(val, 0x8000) ~= 0 and 0x10000 or 0) end --- @type fun(val: number): ImS16 - ---- @alias ImU32 integer --- @alias ImU64 integer --- @alias ImS64 integer diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 070d259..4c9a97a 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -3093,6 +3093,212 @@ function ImGui.ScaleValueFromRatioT(data_type, t, v_min, v_max, logarithmic_zero return result end +--- @generic T : number +--- @param bb ImRect +--- @param id ImGuiID +--- @param data_type ImGuiDataType +--- @param v T +--- @param v_min T +--- @param v_max T +--- @param format string +--- @param flags ImGuiSliderFlags +--- @param out_grab_bb ImRect +--- @return T v # updated `v` passed in +--- @return bool value_changed +function ImGui.SliderBehaviorT(bb, id, data_type, v, v_min, v_max, format, flags, out_grab_bb) + local g = GImGui + local style = g.Style + + local axis = (bit.band(flags, ImGuiSliderFlags.Vertical) ~= 0) and ImGuiAxis.Y or ImGuiAxis.X + local is_logarithmic = bit.band(flags, ImGuiSliderFlags.Logarithmic) ~= 0 + local is_floating_point = (data_type == ImGuiDataType.Float) or (data_type == ImGuiDataType.Double) + local v_range_f = (v_min < v_max) and (v_max - v_min) or (v_min - v_max) + + local grab_padding = 2.0 + local slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0 + local grab_sz = style.GrabMinSize + if not is_floating_point and v_range_f >= 0.0 then + grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize) + end + grab_sz = ImMin(grab_sz, slider_sz) + local slider_usable_sz = slider_sz - grab_sz + local slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5 + local slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5 + + local logarithmic_zero_epsilon = 0.0 + local zero_deadzone_halfsize = 0.0 + if is_logarithmic then + local decimal_precision = is_floating_point and ImParseFormatPrecision(format, 3) or 1 + logarithmic_zero_epsilon = ImPow(0.1, decimal_precision) + zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5) / ImMax(slider_usable_sz, 1.0) + end + + local value_changed = false + if g.ActiveId == id then + local set_new_value = false + local clicked_t = 0.0 + if g.ActiveIdSource == ImGuiInputSource.Mouse then + if not g.IO.MouseDown[0] then + ImGui.ClearActiveID() + else + local mouse_abs_pos = g.IO.MousePos[axis] + if g.ActiveIdIsJustActivated then + local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + if axis == ImGuiAxis.Y then + grab_t = 1.0 - grab_t + end + local grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t) + local clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5 - 1.0) and (mouse_abs_pos <= grab_pos + grab_sz * 0.5 + 1.0) + g.SliderGrabClickOffset = (clicked_around_grab and is_floating_point) and (mouse_abs_pos - grab_pos) or 0.0 + end + if slider_usable_sz > 0.0 then + clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz) + end + if axis == ImGuiAxis.Y then + clicked_t = 1.0 - clicked_t + end + set_new_value = true + end + elseif g.ActiveIdSource == ImGuiInputSource.Keyboard or g.ActiveIdSource == ImGuiInputSource.Gamepad then + if g.ActiveIdIsJustActivated then + g.SliderCurrentAccum = 0.0 + g.SliderCurrentAccumDirty = false + end + + local input_delta = (axis == ImGuiAxis.X) and ImGui.GetNavTweakPressedAmount(axis) or -ImGui.GetNavTweakPressedAmount(axis) + if input_delta ~= 0.0 then + local tweak_slow = ImGui.IsKeyDown((g.NavInputSource == ImGuiInputSource.Gamepad) and ImGuiKey.NavGamepadTweakSlow or ImGuiKey.NavKeyboardTweakSlow) + local tweak_fast = ImGui.IsKeyDown((g.NavInputSource == ImGuiInputSource.Gamepad) and ImGuiKey.NavGamepadTweakFast or ImGuiKey.NavKeyboardTweakFast) + local decimal_precision = is_floating_point and ImParseFormatPrecision(format, 3) or 0 + if decimal_precision > 0 then + input_delta = input_delta / 100.0 + if tweak_slow then + input_delta = input_delta / 10.0 + end + else + if (v_range_f >= -100.0 and v_range_f <= 100.0 and v_range_f ~= 0.0) or tweak_slow then + input_delta = ((input_delta < 0.0) and -1.0 or 1.0) / v_range_f + else + input_delta = input_delta / 100.0 + end + end + if tweak_fast then + input_delta = input_delta * 10.0 + end + + g.SliderCurrentAccum = g.SliderCurrentAccum + input_delta + g.SliderCurrentAccumDirty = true + end + + local delta = g.SliderCurrentAccum + if g.NavActivatePressedId == id and not g.ActiveIdIsJustActivated then + ImGui.ClearActiveID() + elseif g.SliderCurrentAccumDirty then + clicked_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + + if (clicked_t >= 1.0 and delta > 0.0) or (clicked_t <= 0.0 and delta < 0.0) then + set_new_value = false + g.SliderCurrentAccum = 0.0 + else + set_new_value = true + local old_clicked_t = clicked_t + clicked_t = ImSaturate(clicked_t + delta) + + local v_new = ImGui.ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + if is_floating_point and bit.band(flags, ImGuiSliderFlags.NoRoundToFormat) == 0 then + v_new = ImGui.RoundScalarWithFormatT(format, data_type, v_new) + end + local new_clicked_t = ImGui.ScaleRatioFromValueT(data_type, v_new, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + + if delta > 0 then + g.SliderCurrentAccum = g.SliderCurrentAccum - ImMin(new_clicked_t - old_clicked_t, delta) + else + g.SliderCurrentAccum = g.SliderCurrentAccum - ImMax(new_clicked_t - old_clicked_t, delta) + end + end + + g.SliderCurrentAccumDirty = false + end + end + + if set_new_value then + if (bit.band(g.LastItemData.ItemFlags, ImGuiItemFlags.ReadOnly) ~= 0) or (bit.band(flags, ImGuiSliderFlags.ReadOnly) ~= 0) then + set_new_value = false + end + end + + if set_new_value then + local v_new = ImGui.ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + + if is_floating_point and bit.band(flags, ImGuiSliderFlags.NoRoundToFormat) == 0 then + v_new = ImGui.RoundScalarWithFormatT(format, data_type, v_new) + end + + if v ~= v_new then + v = v_new + value_changed = true + end + end + end + + if slider_sz < 1.0 then + ImRect_Copy(out_grab_bb, ImRect(bb.Min, bb.Min)) + else + local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + if axis == ImGuiAxis.Y then + grab_t = 1.0 - grab_t + end + local grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t) + if axis == ImGuiAxis.X then + ImRect_Copy(out_grab_bb, ImRect(grab_pos - grab_sz * 0.5, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5, bb.Max.y - grab_padding)) + else + ImRect_Copy(out_grab_bb, ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5)) + end + end + + return v, value_changed +end + +--- @generic T : number +--- @param bb ImRect +--- @param id ImGuiID +--- @param data_type ImGuiDataType +--- @param v T +--- @param min T +--- @param max T +--- @param format string +--- @param flags ImGuiSliderFlags +--- @param out_grab_bb ImRect +function ImGui.SliderBehavior(bb, id, data_type, v, min, max, format, flags, out_grab_bb) + IM_ASSERT((flags == 1 or bit.band(flags, ImGuiSliderFlags.InvalidMask_) == 0), "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.") + IM_ASSERT(bit.band(flags, ImGuiSliderFlags.WrapAround) == 0) + + if data_type == ImGuiDataType.S8 then + local v32 = (ImS32)((ImS8)(v)) + elseif data_type == ImGuiDataType.U8 then + + elseif data_type == ImGuiDataType.S16 then + + elseif data_type == ImGuiDataType.U16 then + + elseif data_type == ImGuiDataType.S32 then + + elseif data_type == ImGuiDataType.U32 then + + elseif data_type == ImGuiDataType.S64 then + + elseif data_type == ImGuiDataType.U64 then + + elseif data_type == ImGuiDataType.Float then + + elseif data_type == ImGuiDataType.Double then + + end + + IM_ASSERT(false) + return v, false +end + ---------------------------------------------------------------- -- [SECTION] INPUT TEXT ---------------------------------------------------------------- From 48d0a0b62b721b618b56fc9010a9b23fb2ae4afd Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:10:13 +0800 Subject: [PATCH 02/22] Make stb_truetype use casting helpers already defined --- lua/imstb_truetype.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lua/imstb_truetype.lua b/lua/imstb_truetype.lua index de8773e..c2beac2 100644 --- a/lua/imstb_truetype.lua +++ b/lua/imstb_truetype.lua @@ -71,13 +71,13 @@ local function STBTT__NOTUSED(_) return end --- @alias stbtt_int32 int -local function stbtt_int32(val) return bit.band(val, 0xFFFFFFFF) - (bit.band(val, 0x80000000) ~= 0 and 0x100000000 or 0) end -local function stbtt_uint32(val) return bit.band(val, 0xFFFFFFFF) end -local function stbtt_int16(val) return bit.band(val, 0xFFFF) - (bit.band(val, 0x8000) ~= 0 and 0x10000 or 0) end -local function stbtt_uint16(val) return bit.band(val, 0xFFFF) end -local function stbtt_int8(val) return bit.band(val, 0xFF) - (bit.band(val, 0x80) ~= 0 and 0x100 or 0) end -local function stbtt_uint8(val) return bit.band(val, 0xFF) end -local function unsigned_char(val) return bit.band(val, 0xFF) end +local stbtt_int32 = ImS32 +local stbtt_uint32 = ImU32 +local stbtt_int16 = ImS16 +local stbtt_uint16 = ImU16 +local stbtt_int8 = ImS8 +local stbtt_uint8 = ImU8 +local unsigned_char = stbtt_uint8 --- @class stbtt__buf --- @field data? table # 1-based byte table From 2c1dc99a5bc106568fb5f8510ebcd977c15892fb Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:38:08 +0800 Subject: [PATCH 03/22] Finish `SliderBehavior()` impl --- lua/imgui_widgets.lua | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 4c9a97a..77302a4 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -3269,30 +3269,47 @@ end --- @param format string --- @param flags ImGuiSliderFlags --- @param out_grab_bb ImRect +--- @return T v +--- @return bool value_changed function ImGui.SliderBehavior(bb, id, data_type, v, min, max, format, flags, out_grab_bb) IM_ASSERT((flags == 1 or bit.band(flags, ImGuiSliderFlags.InvalidMask_) == 0), "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.") IM_ASSERT(bit.band(flags, ImGuiSliderFlags.WrapAround) == 0) if data_type == ImGuiDataType.S8 then - local v32 = (ImS32)((ImS8)(v)) + local v32 = (ImS32)((ImS8)(v)); local r + v32, r = ImGui.SliderBehaviorT(bb, id, ImGuiDataType.S32, v32, min, max, format, flags, out_grab_bb) + if r then v = (ImS8)(v32) end + return v, r elseif data_type == ImGuiDataType.U8 then - + local v32 = (ImU32)((ImU8)(v)); local r + v32, r = ImGui.SliderBehaviorT(bb, id, ImGuiDataType.U32, v32, min, max, format, flags, out_grab_bb) + if r then v = (ImU8)(v32) end + return v, r elseif data_type == ImGuiDataType.S16 then - + local v32 = (ImS32)((ImS16)(v)); local r + v32, r = ImGui.SliderBehaviorT(bb, id, ImGuiDataType.S32, v32, min, max, format, flags, out_grab_bb) + if r then v = (ImS16)(v32) end + return v, r elseif data_type == ImGuiDataType.U16 then - + local v32 = (ImU32)((ImU16)(v)); local r + v32, r = ImGui.SliderBehaviorT(bb, id, ImGuiDataType.U32, v32, min, max, format, flags, out_grab_bb) + if r then v = (ImU16)(v32) end + return v, r elseif data_type == ImGuiDataType.S32 then - + IM_ASSERT(v_min >= math.floor(IM_S32_MIN / 2) and v_max <= math.floor(IM_S32_MAX / 2)) + return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.U32 then - + IM_ASSERT(v_max <= math.floor(IM_U32_MAX / 2)) + return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.S64 then - - elseif data_type == ImGuiDataType.U64 then - + IM_ASSERT(v_min >= math.floor(IM_S64_MIN / 2) and v_max <= math.floor(IM_S64_MAX / 2)) + return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.Float then - + IM_ASSERT(v_min >= -FLT_MAX / 2.0 and v_max <= FLT_MAX / 2.0) + return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.Double then - + IM_ASSERT(v_min >= -DBL_MAX / 2.0 and v_max <= DBL_MAX / 2.0) + return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) end IM_ASSERT(false) From f9b421f747c74b33dbf5bf3164a523355b009ecc Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Wed, 10 Jun 2026 16:59:18 +0800 Subject: [PATCH 04/22] Implement `SliderScalar()` --- lua/imgui_widgets.lua | 106 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 77302a4..21cc06e 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -2901,6 +2901,8 @@ function ImGui.DragScalar(label, data_type, data, v_speed, min, max, format, fla ImGui.MarkItemEdited(id) end + -- TODO: ImGui.DataTypeFormatString() should be used here instead of ImFormatString() + -- Display value using user-provided display format so user can add prefix/suffix/decorations to the value ImGui.RenderTextClipped(frame_bb.Min, frame_bb.Max, ImFormatString(format, data), nil, nil, ImVec2(0.5, 0.5)) @@ -3316,6 +3318,110 @@ function ImGui.SliderBehavior(bb, id, data_type, v, min, max, format, flags, out return v, false end +--- @generic T : number +--- @param label string +--- @param data_type ImGuiDataType +--- @param data T +--- @param min T +--- @param max T +--- @param format? string +--- @param flags? ImGuiSliderFlags +function ImGui.SliderScalar(label, data_type, data, min, max, format, flags) + if flags == nil then flags = 0 end + + local window = ImGui.GetCurrentWindow() + if window.SkipItems then + return data, false + end + + local g = GImGui + local style = g.Style + local id = window:GetID(label) + local w = ImGui.CalcItemWidth() + local color_marker = (bit.band(g.NextItemData.HasFlags, ImGuiNextItemDataFlags.HasColorMarker) ~= 0) and g.NextItemData.ColorMarker or 0 + + local label_end = ImGui.FindRenderedTextEnd(label) + local label_size = ImGui.CalcTextSize(label, label_end, false) + local frame_bb = ImRect(window.DC.CursorPos, window.DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0)) + local total_bb_x = frame_bb.Max.x + ((label_size.x > 0.0) and (style.ItemInnerSpacing.x + label_size.x) or 0.0) + local total_bb = ImRect(frame_bb.Min, ImVec2(total_bb_x, frame_bb.Max.y)) + + local temp_input_allowed = bit.band(flags, ImGuiSliderFlags.NoInput) == 0 + ImGui.ItemSize(total_bb, style.FramePadding.y) + local item_flags = temp_input_allowed and ImGuiItemFlags.Inputable or 0 + if not ImGui.ItemAdd(total_bb, id, frame_bb, item_flags) then + return data, false + end + + if format == nil then + format = ImGui.DataTypeGetInfo(data_type).PrintFmt + end + + local hovered = ImGui.ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags) + local temp_input_is_active = temp_input_allowed and ImGui.TempInputIsActive(id) + if not temp_input_is_active then + local clicked = hovered and ImGui.IsMouseClickedEx(0, ImGuiInputFlags.None, id) + local make_active = clicked or g.NavActivateId == id + if make_active and clicked then + ImGui.SetKeyOwner(ImGuiKey.MouseLeft, id) + end + if make_active and temp_input_allowed then + if (clicked and g.IO.KeyCtrl) or (g.NavActivateId == id and bit.band(g.NavActivateFlags, ImGuiActivateFlags.PreferInput) ~= 0) then + temp_input_is_active = true + end + end + + if make_active then + g.ActiveIdValueOnActivation = data + end + + if make_active and not temp_input_is_active then + ImGui.SetActiveID(id, window) + ImGui.SetFocusID(id, window) + ImGui.FocusWindow(window) + g.ActiveIdUsingNavDirMask = bit.bor(g.ActiveIdUsingNavDirMask, bit.lshift(1, ImGuiDir.Left), bit.lshift(1, ImGuiDir.Right)) + end + end + + if temp_input_is_active then + local clamp_enabled = bit.band(flags, ImGuiSliderFlags.ClampOnInput) ~= 0 + return ImGui.TempInputScalar(frame_bb, id, label, data_type, data, format, clamp_enabled and min or nil, clamp_enabled and max or nil) + end + + local frame_col = ImGui.GetColorU32((g.ActiveId == id) and ImGuiCol.FrameBgActive or (hovered and ImGuiCol.FrameBgHovered or ImGuiCol.FrameBg)) + ImGui.RenderNavCursor(frame_bb, id) + ImGui.RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, false, style.FrameRounding) + if color_marker ~= 0 and style.ColorMarkerSize > 0.0 then + ImGui.RenderColorComponentMarker(frame_bb, ImGui.GetColorU32_U32(color_marker), style.FrameRounding) + end + ImGui.RenderFrameBorder(frame_bb.Min, frame_bb.Max, g.Style.FrameRounding) + + local grab_bb = ImRect() + local value_changed + data, value_changed = ImGui.SliderBehavior(frame_bb, id, data_type, data, min, max, format, flags, grab_bb) + if value_changed then + ImGui.MarkItemEdited(id) + end + + if grab_bb.Max.x > grab_bb.Min.x then + window.DrawList:AddRectFilled(grab_bb.Min, grab_bb.Max, ImGui.GetColorU32((g.ActiveId == id) and ImGuiCol.SliderGrabActive or ImGuiCol.SliderGrab), style.GrabRounding) + end + + -- TODO: ImGui.DataTypeFormatString() should be used here instead of ImFormatString() + + -- if g.LogEnabled then + -- ImGui.LogSetNextTextDecoration("{", "}") + -- end + ImGui.RenderTextClipped(frame_bb.Min, frame_bb.Max, ImFormatString(format, data), nil, nil, ImVec2(0.5, 0.5)) + + if label_size.x > 0.0 then + ImGui.RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label, 1, label_end, false) + end + + -- IMGUI_TEST_ENGINE_ITEM_INFO() + return data, value_changed +end + ---------------------------------------------------------------- -- [SECTION] INPUT TEXT ---------------------------------------------------------------- From 18f033a3adf620b38427605c3e66224a71cc7775 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Wed, 10 Jun 2026 18:18:57 +0800 Subject: [PATCH 05/22] Implement `SliderFloat()` and fix some bugs to get it working... Temporarily fixed `RoundScalarWithFormatT()` using pattern matching --- lua/imgui_demo.lua | 16 ++++++++++++++++ lua/imgui_widgets.lua | 37 ++++++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/lua/imgui_demo.lua b/lua/imgui_demo.lua index 5a813d4..89861ca 100644 --- a/lua/imgui_demo.lua +++ b/lua/imgui_demo.lua @@ -173,6 +173,9 @@ table.insert(hint0, 0) local i0 = 233 +local f1 = 0.123 +local f2 = 0.0 + function DemoWindowWidgetsBasic() if ImGui.TreeNode("Basic") then ImGui.SeparatorText("General") @@ -247,6 +250,19 @@ function DemoWindowWidgetsBasic() -- TODO: end + ImGui.SeparatorText("Drags") + + do + + end + + ImGui.SeparatorText("Sliders") + + do + f1 = ImGui.SliderFloat("slider float", f1, 0.0, 1.0, "ratio = %.3f") + f2 = ImGui.SliderFloat("slider float (log)", f2, -10.0, 10.0, "%.4f", ImGuiSliderFlags.Logarithmic) + end + ImGui.TreePop() end end diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 21cc06e..22df239 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -2238,7 +2238,9 @@ function ImGui.RoundScalarWithFormatT(format, data_type, v) -- Currently does nothing to sanitize local str = ImFormatString(format, v) - v = tonumber(str) --[[@as number]] + -- This is a temporary solution + -- cpp uses ImAtof which scans past prefix... + v = tonumber(str:match("[+-]?%d*%.?%d+")) or v return v end @@ -3298,20 +3300,20 @@ function ImGui.SliderBehavior(bb, id, data_type, v, min, max, format, flags, out if r then v = (ImU16)(v32) end return v, r elseif data_type == ImGuiDataType.S32 then - IM_ASSERT(v_min >= math.floor(IM_S32_MIN / 2) and v_max <= math.floor(IM_S32_MAX / 2)) - return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) + IM_ASSERT(min >= math.floor(IM_S32_MIN / 2) and max <= math.floor(IM_S32_MAX / 2)) + return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.U32 then - IM_ASSERT(v_max <= math.floor(IM_U32_MAX / 2)) - return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) + IM_ASSERT(max <= math.floor(IM_U32_MAX / 2)) + return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.S64 then - IM_ASSERT(v_min >= math.floor(IM_S64_MIN / 2) and v_max <= math.floor(IM_S64_MAX / 2)) - return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) + IM_ASSERT(min >= math.floor(IM_S64_MIN / 2) and max <= math.floor(IM_S64_MAX / 2)) + return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.Float then - IM_ASSERT(v_min >= -FLT_MAX / 2.0 and v_max <= FLT_MAX / 2.0) - return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) + IM_ASSERT(min >= -FLT_MAX / 2.0 and max <= FLT_MAX / 2.0) + return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.Double then - IM_ASSERT(v_min >= -DBL_MAX / 2.0 and v_max <= DBL_MAX / 2.0) - return ImGui.SliderBehaviorT(bb, id, data_type, v32, min, max, format, flags, out_grab_bb) + IM_ASSERT(min >= -DBL_MAX / 2.0 and max <= DBL_MAX / 2.0) + return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) end IM_ASSERT(false) @@ -3422,6 +3424,19 @@ function ImGui.SliderScalar(label, data_type, data, min, max, format, flags) return data, value_changed end +--- @param label string +--- @param v float +--- @param v_min float +--- @param v_max float +--- @param format? string +--- @param flags? ImGuiSliderFlags +function ImGui.SliderFloat(label, v, v_min, v_max, format, flags) + if format == nil then format = "%.3f" end + if flags == nil then flags = 0 end + + return ImGui.SliderScalar(label, ImGuiDataType.Float, v, v_min, v_max, format, flags) +end + ---------------------------------------------------------------- -- [SECTION] INPUT TEXT ---------------------------------------------------------------- From f6a7a678e4c3083b4f373a4ef7e1a55be7ca5b85 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Thu, 11 Jun 2026 10:38:50 +0800 Subject: [PATCH 06/22] Add `ImAtof()` stub and use it --- lua/imgui_internal.lua | 6 ++++++ lua/imgui_widgets.lua | 8 +++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lua/imgui_internal.lua b/lua/imgui_internal.lua index 44bb152..01fbb31 100644 --- a/lua/imgui_internal.lua +++ b/lua/imgui_internal.lua @@ -187,6 +187,12 @@ function ImStd.ImExponentialMovingAverage(avg, sample, n) return avg end +-- Uses `string.match` internally +--- @param str string +function ImAtof(str) + return tonumber(string.match(str, "[+-]?%d*%.?%d+")) +end + --- @param s table # 1-based --- @param c any --- @param n int diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 22df239..def6518 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -2234,13 +2234,11 @@ function ImGui.RoundScalarWithFormatT(format, data_type, v) return v -- Don't apply if the value is not visible in the format string end - -- Sanitize format - -- Currently does nothing to sanitize + -- TODO: Sanitize format local str = ImFormatString(format, v) - -- This is a temporary solution - -- cpp uses ImAtof which scans past prefix... - v = tonumber(str:match("[+-]?%d*%.?%d+")) or v + + v = ImAtof(str) or v return v end From df304d6fd73d1d528b5b12ea6cfb978422a5cc7c Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:13:59 +0800 Subject: [PATCH 07/22] Add buggy `SliderInt()` --- lua/imgui_demo.lua | 7 +++++++ lua/imgui_widgets.lua | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/lua/imgui_demo.lua b/lua/imgui_demo.lua index 89861ca..dad5652 100644 --- a/lua/imgui_demo.lua +++ b/lua/imgui_demo.lua @@ -176,6 +176,10 @@ local i0 = 233 local f1 = 0.123 local f2 = 0.0 +local Element = { Fire = 1, Earth = 2, Air = 3, Water = 4, COUNT = 5 } +local elems_names = { "Fire", "Earth", "Air", "Water" } +local elem = Element.Fire + function DemoWindowWidgetsBasic() if ImGui.TreeNode("Basic") then ImGui.SeparatorText("General") @@ -261,6 +265,9 @@ function DemoWindowWidgetsBasic() do f1 = ImGui.SliderFloat("slider float", f1, 0.0, 1.0, "ratio = %.3f") f2 = ImGui.SliderFloat("slider float (log)", f2, -10.0, 10.0, "%.4f", ImGuiSliderFlags.Logarithmic) + + local elem_name = (elem >= 1 and elem < Element.COUNT) and elems_names[elem] or "Unknown" + elem = ImGui.SliderInt("slider enum", elem, 1, Element.COUNT - 1, elem_name) end ImGui.TreePop() diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index def6518..72d255a 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -3435,6 +3435,19 @@ function ImGui.SliderFloat(label, v, v_min, v_max, format, flags) return ImGui.SliderScalar(label, ImGuiDataType.Float, v, v_min, v_max, format, flags) end +--- @param label string +--- @param v int +--- @param v_min int +--- @param v_max int +--- @param format? string +--- @param flags? ImGuiSliderFlags +function ImGui.SliderInt(label, v, v_min, v_max, format, flags) + if format == nil then format = "%d" end + if flags == nil then flags = 0 end + + return ImGui.SliderScalar(label, ImGuiDataType.S32, v, v_min, v_max, format, flags) +end + ---------------------------------------------------------------- -- [SECTION] INPUT TEXT ---------------------------------------------------------------- From 932e89e84dd465a11b7c91ff708a07ceeaba6d97 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Thu, 11 Jun 2026 12:58:02 +0800 Subject: [PATCH 08/22] Seem to have fixed `SliderInt()` by patching `ScaleValueFromRatioT()` --- lua/imgui_widgets.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 72d255a..5a8b4cc 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -3076,6 +3076,11 @@ function ImGui.ScaleValueFromRatioT(data_type, t, v_min, v_max, logarithmic_zero else result = v_min_fudged * ImPow(v_max_fudged / v_min_fudged, t_with_flip) end + + -- LUA: simulate cpp template `TYPE` return cast for integer types + if data_type ~= ImGuiDataType.Float and data_type ~= ImGuiDataType.Double then + result = ImTrunc(result) + end else -- Linear slider local is_floating_point = (data_type == ImGuiDataType.Float) or (data_type == ImGuiDataType.Double) @@ -3086,9 +3091,10 @@ function ImGui.ScaleValueFromRatioT(data_type, t, v_min, v_max, logarithmic_zero -- This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. -- - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64 -- range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits. - local v_new_off_f = (v_max - v_min) * t - local offset = (v_min > v_max) and -0.5 or 0.5 - result = v_min + v_new_off_f + offset + + -- LUA: use `ImTrunc` explicitly here to get close to `SIGNEDTYPE` template type cast in cpp + local v_new_off_f = ImTrunc((v_max - v_min) * t) + result = ImTrunc(v_min) + ImTrunc(v_new_off_f + ((v_min > v_max) and -0.5 or 0.5)) end end From 8bf33715f7599b4c1f700d2438223891907b4af6 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Thu, 11 Jun 2026 17:10:06 +0800 Subject: [PATCH 09/22] Improve the patch in `ScaleValueFromRatioT()` --- lua/imgui_widgets.lua | 44 +++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 5a8b4cc..4bcc5a6 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -1947,6 +1947,28 @@ end -- [SECTION] DATA TYPE & DATA FORMATTING [Internal] ---------------------------------------------------------------- +local DATA_TYPE, TYPE, SIGNEDTYPE do + local _TYPE = nil + + --- @param data_type ImGuiDataType + function DATA_TYPE(data_type) + _TYPE = data_type + end + + --- @generic T : number + --- @param val T + --- @return T + function TYPE(val) + IM_ASSERT(_TYPE ~= nil) + if _TYPE ~= ImGuiDataType.Float and _TYPE ~= ImGuiDataType.Double then + return ImTrunc(val) + end + return val + end + + SIGNEDTYPE = TYPE +end + local GDefaultRgbaColorMarkers = { IM_COL32(240, 20, 20, 255), IM_COL32(20, 240, 20, 255), IM_COL32(20, 20, 240, 255), IM_COL32(140, 140, 140, 255) } @@ -3022,6 +3044,7 @@ end --- @param logarithmic_zero_epsilon float --- @param zero_deadzone_halfsize float function ImGui.ScaleValueFromRatioT(data_type, t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) +DATA_TYPE(data_type) -- We special-case the extents because otherwise our logarithmic fudging can lead to "mathematically correct" -- but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler. if t <= 0.0 or v_min == v_max then @@ -3065,21 +3088,16 @@ function ImGui.ScaleValueFromRatioT(data_type, t, v_min, v_max, logarithmic_zero local zero_point_snap_R = zero_point_center + zero_deadzone_halfsize if t_with_flip >= zero_point_snap_L and t_with_flip <= zero_point_snap_R then - result = 0.0 -- Special case to make getting exactly zero possible (the epsilon prevents it otherwise) + result = (TYPE)(0.0) -- Special case to make getting exactly zero possible (the epsilon prevents it otherwise) elseif t_with_flip < zero_point_center then - result = -(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, 1.0 - (t_with_flip / zero_point_snap_L))) + result = (TYPE)(-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, 1.0 - (t_with_flip / zero_point_snap_L)))) else - result = logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (t_with_flip - zero_point_snap_R) / (1.0 - zero_point_snap_R)) + result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (t_with_flip - zero_point_snap_R) / (1.0 - zero_point_snap_R))) end elseif v_min < 0.0 or v_max < 0.0 then -- Entirely negative slider - result = -(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, 1.0 - t_with_flip)) + result = (TYPE)(-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, 1.0 - t_with_flip))) else - result = v_min_fudged * ImPow(v_max_fudged / v_min_fudged, t_with_flip) - end - - -- LUA: simulate cpp template `TYPE` return cast for integer types - if data_type ~= ImGuiDataType.Float and data_type ~= ImGuiDataType.Double then - result = ImTrunc(result) + result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, t_with_flip)) end else -- Linear slider @@ -3091,10 +3109,8 @@ function ImGui.ScaleValueFromRatioT(data_type, t, v_min, v_max, logarithmic_zero -- This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. -- - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64 -- range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits. - - -- LUA: use `ImTrunc` explicitly here to get close to `SIGNEDTYPE` template type cast in cpp - local v_new_off_f = ImTrunc((v_max - v_min) * t) - result = ImTrunc(v_min) + ImTrunc(v_new_off_f + ((v_min > v_max) and -0.5 or 0.5)) + local v_new_off_f = (SIGNEDTYPE)((v_max - v_min)) * t + result = (TYPE)((SIGNEDTYPE)(v_min) + (SIGNEDTYPE)(v_new_off_f + ((v_min > v_max) and -0.5 or 0.5))) end end From 4536793f0db2bcc19687f8eac9ff247d8acbc3eb Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Thu, 11 Jun 2026 23:05:28 +0800 Subject: [PATCH 10/22] Add `DragInt()` demo and update its decl. Also fix `ImParseFormatTrimDecorations()` calling `size_t()` for some reason --- lua/imgui_demo.lua | 7 ++++++- lua/imgui_widgets.lua | 22 ++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lua/imgui_demo.lua b/lua/imgui_demo.lua index dad5652..f581077 100644 --- a/lua/imgui_demo.lua +++ b/lua/imgui_demo.lua @@ -1,6 +1,8 @@ --- ImGui Sincerely WIP -- (Demo Code) +local static = {} + local IM_MIN = math.min local IM_MAX = math.max local function IM_CLAMP(V, MN, MX) return (V < MN) and MN or (V > MX) and MX or V end @@ -172,6 +174,8 @@ local hint0 = {string.byte("enter text here", 1, 15)} table.insert(hint0, 0) local i0 = 233 +static.i1 = 50 +static.i2 = 42 local f1 = 0.123 local f2 = 0.0 @@ -257,7 +261,8 @@ function DemoWindowWidgetsBasic() ImGui.SeparatorText("Drags") do - + static.i1 = ImGui.DragInt("drag int", static.i1, 1) + static.i2 = ImGui.DragInt("drag int 0..100", static.i2, 1, 0, 100, "%d%%", ImGuiSliderFlags.AlwaysClamp) end ImGui.SeparatorText("Sliders") diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 4bcc5a6..d2ef81e 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -2321,7 +2321,7 @@ local function ImParseFormatTrimDecorations(fmt, buf, buf_size) if string.byte(fmt, fmt_end) ~= 37 then return fmt_start end - local n = ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size) + local n = ImMin((fmt_end - fmt_start) + 1, buf_size) ImStd.ImStrncpy(buf, 1, { string.byte(fmt, fmt_start, fmt_start + n - 1) }, 1, n) return 1 end @@ -2946,14 +2946,20 @@ function ImGui.DragFloat(label, v, v_speed, v_min, v_max, format, flags) return ImGui.DragScalar(label, ImGuiDataType.Float, v, v_speed, v_min, v_max, format, flags) end ---- @param label string ---- @param v int ---- @param v_speed float ---- @param v_min int ---- @param v_max int ---- @param format string ---- @param flags ImGuiSliderFlags +--- @param label string +--- @param v int +--- @param v_speed? float +--- @param v_min? int +--- @param v_max? int +--- @param format? string +--- @param flags? ImGuiSliderFlags function ImGui.DragInt(label, v, v_speed, v_min, v_max, format, flags) + if v_speed == nil then v_speed = 1.0 end + if v_min == nil then v_min = 0 end + if v_max == nil then v_max = 0 end + if format == nil then format = "%d" end + if flags == nil then flags = 0 end + return ImGui.DragScalar(label, ImGuiDataType.S32, v, v_speed, v_min, v_max, format, flags) end From 1b3be90233ded6377da354e5166d3dbed297268c Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Fri, 12 Jun 2026 19:33:49 +0800 Subject: [PATCH 11/22] Add `DragInt()` WrapAround demo call. It's bugged now --- lua/imgui_demo.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/imgui_demo.lua b/lua/imgui_demo.lua index f581077..da4c4fe 100644 --- a/lua/imgui_demo.lua +++ b/lua/imgui_demo.lua @@ -173,9 +173,10 @@ str1[1] = 0 local hint0 = {string.byte("enter text here", 1, 15)} table.insert(hint0, 0) -local i0 = 233 +static.i0 = 233 static.i1 = 50 static.i2 = 42 +static.i3 = 128 local f1 = 0.123 local f2 = 0.0 @@ -254,7 +255,7 @@ function DemoWindowWidgetsBasic() ImGui.InputTextWithHint("input text (w/ hint)", hint0, str1, 128) - i0 = ImGui.InputInt("input int", i0) + static.i0 = ImGui.InputInt("input int", static.i0) -- TODO: end @@ -263,6 +264,7 @@ function DemoWindowWidgetsBasic() do static.i1 = ImGui.DragInt("drag int", static.i1, 1) static.i2 = ImGui.DragInt("drag int 0..100", static.i2, 1, 0, 100, "%d%%", ImGuiSliderFlags.AlwaysClamp) + static.i3 = ImGui.DragInt("drag int wrap 100..200", static.i3, 1, 100, 200, "%d", ImGuiSliderFlags.WrapAround) end ImGui.SeparatorText("Sliders") From 23b109d953d09ee04a1f31526e84980f9320029d Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:02:17 +0800 Subject: [PATCH 12/22] Remove broken `ImS64` and `ImU64` types, Change `ImGuiDataType` to 0-based. --- lua/imgui_h.lua | 30 +++++++++++++----------------- lua/imgui_internal.lua | 2 -- lua/imgui_widgets.lua | 40 +++++++++++----------------------------- 3 files changed, 24 insertions(+), 48 deletions(-) diff --git a/lua/imgui_h.lua b/lua/imgui_h.lua index 33792e1..1398346 100644 --- a/lua/imgui_h.lua +++ b/lua/imgui_h.lua @@ -38,9 +38,6 @@ function ImU32(val) return _band(val, 0xFFFFFFFF) end --- @return ImS32 function ImS32(val) return _band(val, 0xFFFFFFFF) - (_band(val, 0x80000000) ~= 0 and 0x100000000 or 0) end ---- @alias ImU64 integer - ---- @alias ImS64 integer --- @alias float number --- @alias int integer @@ -58,7 +55,7 @@ function ImS32(val) return _band(val, 0xFFFFFFFF) - (_band(val, 0x80000000) ~= 0 --- @alias ImGuiID unsigned_int ---- @alias ImTextureID ImU64 +--- @alias ImTextureID integer --- @alias ImGuiKeyChord int @@ -1757,21 +1754,20 @@ ImGuiDragDropFlags = { ImGuiDragDropFlags.AcceptPeekOnly = bit.bor(ImGuiDragDropFlags.AcceptBeforeDelivery, ImGuiDragDropFlags.AcceptNoDrawDefaultRect) ---- Note that `U64` isn't supported +--- Note that `S64` and `U64` are not supported --- @enum ImGuiDataType ImGuiDataType = { - S8 = 1, -- signed char / char - U8 = 2, -- unsigned char - S16 = 3, -- short - U16 = 4, -- unsigned short - S32 = 5, -- int - U32 = 6, -- unsigned int - S64 = 7, -- long long / __int64 - Float = 8, -- float - Double = 9, -- double - Bool = 10, -- bool (provided for user convenience, not supported by scalar widgets) - String = 11, -- string (provided for user convenience, not supported by scalar widgets) - COUNT = 11 + S8 = 0, -- signed char / char + U8 = 1, -- unsigned char + S16 = 2, -- short + U16 = 3, -- unsigned short + S32 = 4, -- int + U32 = 5, -- unsigned int + Float = 6, -- float + Double = 7, -- double + Bool = 8, -- bool (provided for user convenience, not supported by scalar widgets) + String = 9, -- string (provided for user convenience, not supported by scalar widgets) + COUNT = 10 } IM_COL32_R_SHIFT = 0 diff --git a/lua/imgui_internal.lua b/lua/imgui_internal.lua index 01fbb31..a67b585 100644 --- a/lua/imgui_internal.lua +++ b/lua/imgui_internal.lua @@ -32,8 +32,6 @@ DBL_MAX = 1.7976931348623e+308 INT_MIN = -0x7fffffff - 1 INT_MAX = 0x7fffffff UINT_MAX = 0x7fffffff * 2 + 1 -LLONG_MIN = -9223372036854775807 - 1 -LLONG_MAX = 9223372036854775807 IM_PI = math.pi ImPow = math.pow diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index d2ef81e..de71504 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -25,8 +25,6 @@ local IM_S32_MIN = INT_MIN local IM_S32_MAX = INT_MAX local IM_U32_MIN = 0 local IM_U32_MAX = UINT_MAX -local IM_S64_MIN = LLONG_MIN -local IM_S64_MAX = LLONG_MAX ---------------------------------------------------------------- -- [SECTION] TEXT @@ -818,12 +816,12 @@ end --- @param bb_frame ImRect --- @param id ImGuiID --- @param axis ImGuiAxis ---- @param p_scroll_v ImS64 ---- @param size_visible_v ImS64 ---- @param size_contents_v ImS64 +--- @param p_scroll_v number +--- @param size_visible_v number +--- @param size_contents_v number --- @param draw_rounding_flags ImDrawFlags ---- @return bool is_held ---- @return ImS64 scroll_v # Updated p_scroll_v +--- @return bool is_held +--- @return number scroll_v # Updated p_scroll_v function ImGui.ScrollbarEx(bb_frame, id, axis, p_scroll_v, size_visible_v, size_contents_v, draw_rounding_flags) local g = GImGui local window = g.CurrentWindow @@ -1991,7 +1989,6 @@ local GDataTypeInfo = { ImGuiDataTypeInfo(2, "U16", "%u", "%u"), ImGuiDataTypeInfo(4, "S32", "%d", "%d"), ImGuiDataTypeInfo(4, "U32", "%u", "%u"), - ImGuiDataTypeInfo(8, "S64", "%lld", "%lld"), ImGuiDataTypeInfo(4, "float", "%.3f", "%f"), ImGuiDataTypeInfo(8, "double", "%f", "%lf"), ImGuiDataTypeInfo(1, "bool", "%d", "%d"), @@ -2000,8 +1997,8 @@ local GDataTypeInfo = { --- @param data_type ImGuiDataType function ImGui.DataTypeGetInfo(data_type) - IM_ASSERT(data_type >= 1 and data_type <= ImGuiDataType.COUNT) - return GDataTypeInfo[data_type] + IM_ASSERT(data_type >= 0 and data_type < ImGuiDataType.COUNT) + return GDataTypeInfo[data_type + 1] end --- @param buf char[] @@ -2013,8 +2010,6 @@ function ImGui.DataTypeFormatString(buf, buf_size, data_type, data, format) local str if data_type == ImGuiDataType.S32 or data_type == ImGuiDataType.U32 then str = ImFormatString(format, data) - elseif data_type == ImGuiDataType.S64 then - str = ImFormatString(format, data) elseif data_type == ImGuiDataType.Float then str = ImFormatString(format, data) elseif data_type == ImGuiDataType.Double then @@ -2076,12 +2071,6 @@ function ImGui.DataTypeApplyOp(data_type, op, arg1, arg2) elseif op == 45 then return ImSubClampOverflow(arg1, arg2, IM_U32_MIN, IM_U32_MAX) end - elseif data_type == ImGuiDataType.S64 then - if op == 43 then - return ImAddClampOverflow(arg1, arg2, IM_S64_MIN, IM_S64_MAX) - elseif op == 45 then - return ImSubClampOverflow(arg1, arg2, IM_S64_MIN, IM_S64_MAX) - end elseif data_type == ImGuiDataType.Float then if op == 43 then return arg1 + arg2 @@ -2177,9 +2166,8 @@ function ImGui.DataTypeCompare(data_type, arg1, arg2) elseif data_type == ImGuiDataType.U8 then return DataTypeCompareT((ImU8)(arg1), (ImU8)(arg2)) elseif data_type == ImGuiDataType.S16 then return DataTypeCompareT((ImS16)(arg1), (ImS16)(arg2)) elseif data_type == ImGuiDataType.U16 then return DataTypeCompareT((ImU16)(arg1), (ImU16)(arg2)) - elseif data_type == ImGuiDataType.S32 then return DataTypeCompareT((arg1), (arg2)) - elseif data_type == ImGuiDataType.U32 then return DataTypeCompareT((arg1), (arg2)) - elseif data_type == ImGuiDataType.S64 then return DataTypeCompareT((arg1), (arg2)) + elseif data_type == ImGuiDataType.S32 then return DataTypeCompareT((ImS32)(arg1), (ImS32)(arg2)) + elseif data_type == ImGuiDataType.U32 then return DataTypeCompareT((ImU32)(arg1), (ImU32)(arg2)) elseif data_type == ImGuiDataType.Float then return DataTypeCompareT(arg1, arg2) elseif data_type == ImGuiDataType.Double then return DataTypeCompareT(arg1, arg2) end @@ -2207,9 +2195,8 @@ function ImGui.DataTypeClamp(data_type, data, min, max) elseif data_type == ImGuiDataType.U8 then return DataTypeClampT((ImU8)(data), min and (ImU8)(min), max and (ImU8)(max)) elseif data_type == ImGuiDataType.S16 then return DataTypeClampT((ImS16)(data), min and (ImS16)(min), max and (ImS16)(max)) elseif data_type == ImGuiDataType.U16 then return DataTypeClampT((ImU16)(data), min and (ImU16)(min), max and (ImU16)(max)) - elseif data_type == ImGuiDataType.S32 then return DataTypeClampT(data, min, max) - elseif data_type == ImGuiDataType.U32 then return DataTypeClampT(data, min, max) - elseif data_type == ImGuiDataType.S64 then return DataTypeClampT(data, min, max) + elseif data_type == ImGuiDataType.S32 then return DataTypeClampT((ImS32)(data), min and (ImS32)(min), max and (ImS32)(max)) + elseif data_type == ImGuiDataType.U32 then return DataTypeClampT((ImU32)(data), min and (ImU32)(min), max and (ImU32)(max)) elseif data_type == ImGuiDataType.Float then return DataTypeClampT(data, min, max) elseif data_type == ImGuiDataType.Double then return DataTypeClampT(data, min, max) end @@ -2795,8 +2782,6 @@ function ImGui.DragBehavior(id, data_type, v, v_speed, min, max, format, flags) return ImGui.DragBehaviorT(data_type, v, v_speed, min and min or IM_S32_MIN, max and max or IM_S32_MAX, format, flags) elseif data_type == ImGuiDataType.U32 then return ImGui.DragBehaviorT(data_type, v, v_speed, min and min or IM_U32_MIN, max and max or IM_U32_MAX, format, flags) - elseif data_type == ImGuiDataType.S64 then - return ImGui.DragBehaviorT(data_type, v, v_speed, min and min or IM_S64_MIN, max and max or IM_S64_MAX, format, flags) elseif data_type == ImGuiDataType.Float then return ImGui.DragBehaviorT(data_type, v, v_speed, min and min or -FLT_MAX, max and max or FLT_MAX, format, flags) elseif data_type == ImGuiDataType.Double then @@ -3331,9 +3316,6 @@ function ImGui.SliderBehavior(bb, id, data_type, v, min, max, format, flags, out elseif data_type == ImGuiDataType.U32 then IM_ASSERT(max <= math.floor(IM_U32_MAX / 2)) return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) - elseif data_type == ImGuiDataType.S64 then - IM_ASSERT(min >= math.floor(IM_S64_MIN / 2) and max <= math.floor(IM_S64_MAX / 2)) - return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.Float then IM_ASSERT(min >= -FLT_MAX / 2.0 and max <= FLT_MAX / 2.0) return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) From be943cf7e0a408002c008eb4c28f5e4b239e7536 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:07:56 +0800 Subject: [PATCH 13/22] Update previous DataTypeFormat func. Amend comment --- lua/imgui_h.lua | 16 ++++++++-------- lua/imgui_widgets.lua | 10 ++++++---- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lua/imgui_h.lua b/lua/imgui_h.lua index 1398346..4e1b8b6 100644 --- a/lua/imgui_h.lua +++ b/lua/imgui_h.lua @@ -1757,14 +1757,14 @@ ImGuiDragDropFlags.AcceptPeekOnly = bit.bor(ImGuiDragDropFlags.AcceptBeforeDeliv --- Note that `S64` and `U64` are not supported --- @enum ImGuiDataType ImGuiDataType = { - S8 = 0, -- signed char / char - U8 = 1, -- unsigned char - S16 = 2, -- short - U16 = 3, -- unsigned short - S32 = 4, -- int - U32 = 5, -- unsigned int - Float = 6, -- float - Double = 7, -- double + S8 = 0, -- signed char / char + U8 = 1, -- unsigned char + S16 = 2, -- short + U16 = 3, -- unsigned short + S32 = 4, -- int + U32 = 5, -- unsigned int + Float = 6, -- float + Double = 7, -- double Bool = 8, -- bool (provided for user convenience, not supported by scalar widgets) String = 9, -- string (provided for user convenience, not supported by scalar widgets) COUNT = 10 diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index de71504..07ad9b8 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -2008,16 +2008,18 @@ end --- @param format string function ImGui.DataTypeFormatString(buf, buf_size, data_type, data, format) local str - if data_type == ImGuiDataType.S32 or data_type == ImGuiDataType.U32 then - str = ImFormatString(format, data) + if data_type == ImGuiDataType.S32 then + str = ImFormatString(format, (ImS32)(data)) + elseif data_type == ImGuiDataType.U32 then + str = ImFormatString(format, (ImU32)(data)) elseif data_type == ImGuiDataType.Float then str = ImFormatString(format, data) elseif data_type == ImGuiDataType.Double then str = ImFormatString(format, data) elseif data_type == ImGuiDataType.S8 then - str = ImFormatString(format, data) + str = ImFormatString(format, (ImS8)(data)) elseif data_type == ImGuiDataType.U8 then - str = ImFormatString(format, data) + str = ImFormatString(format, (ImU8)(data)) elseif data_type == ImGuiDataType.S16 then str = ImFormatString(format, (ImS16)(data)) elseif data_type == ImGuiDataType.U16 then From 092f0cf2e77ab04d90b132bddd6b22485de1214e Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Sat, 13 Jun 2026 11:27:33 +0800 Subject: [PATCH 14/22] Attempt to fix `DragInt` wrapping bug. Update function template logic. `ImU32()` and `ImS32()` become the same after this commit. Really correct? The `ImS32()` had always been bugged, until this change, wtf? --- lua/imgui_h.lua | 3 +- lua/imgui_widgets.lua | 109 +++++++++++++++++++----------------------- 2 files changed, 52 insertions(+), 60 deletions(-) diff --git a/lua/imgui_h.lua b/lua/imgui_h.lua index 4e1b8b6..19cf02e 100644 --- a/lua/imgui_h.lua +++ b/lua/imgui_h.lua @@ -3,6 +3,7 @@ --- @meta +-- [LuaBitOp](https://bitop.luajit.org/semantics.html) local _band = bit.band --- @class char : integer @@ -36,7 +37,7 @@ function ImS16(val) return _band(val, 0xFFFF) - (_band(val, 0x8000) ~= 0 and 0x1 function ImU32(val) return _band(val, 0xFFFFFFFF) end --- @param val number --- @return ImS32 -function ImS32(val) return _band(val, 0xFFFFFFFF) - (_band(val, 0x80000000) ~= 0 and 0x100000000 or 0) end +function ImS32(val) return _band(val, 0xFFFFFFFF) end --- @alias float number diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 07ad9b8..0975e3f 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -1945,27 +1945,8 @@ end -- [SECTION] DATA TYPE & DATA FORMATTING [Internal] ---------------------------------------------------------------- -local DATA_TYPE, TYPE, SIGNEDTYPE do - local _TYPE = nil - - --- @param data_type ImGuiDataType - function DATA_TYPE(data_type) - _TYPE = data_type - end - - --- @generic T : number - --- @param val T - --- @return T - function TYPE(val) - IM_ASSERT(_TYPE ~= nil) - if _TYPE ~= ImGuiDataType.Float and _TYPE ~= ImGuiDataType.Double then - return ImTrunc(val) - end - return val - end - - SIGNEDTYPE = TYPE -end +local function float(val) return val end +local function double(val) return val end local GDefaultRgbaColorMarkers = { IM_COL32(240, 20, 20, 255), IM_COL32(20, 240, 20, 255), IM_COL32(20, 20, 240, 255), IM_COL32(140, 140, 140, 255) @@ -2595,16 +2576,18 @@ function ImGui.InputInt(label, v, step, step_fast, flags) end -- This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) ---- @param data_type ImGuiDataType ---- @param v number ---- @param v_speed float ---- @param v_min number ---- @param v_max number ---- @param format string ---- @param flags ImGuiSliderFlags +--- @param TYPE function +--- @param SIGNEDTYPE function +--- @param data_type ImGuiDataType +--- @param v number +--- @param v_speed float +--- @param v_min number +--- @param v_max number +--- @param format string +--- @param flags ImGuiSliderFlags --- @return number new_v # Updated `v` --- @return bool changed -function ImGui.DragBehaviorT(data_type, v, v_speed, v_min, v_max, format, flags) +function ImGui.DragBehaviorT(TYPE, SIGNEDTYPE, data_type, v, v_speed, v_min, v_max, format, flags) local g = GImGui local axis = (bit.band(flags, ImGuiSliderFlags.Vertical) ~= 0) and ImGuiAxis.Y or ImGuiAxis.X local is_bounded = (v_min < v_max) or ((v_min == v_max) and (v_min ~= 0.0 or (bit.band(flags, ImGuiSliderFlags.ClampZeroRange) ~= 0))) @@ -2679,7 +2662,7 @@ function ImGui.DragBehaviorT(data_type, v, v_speed, v_min, v_max, format, flags) return v, false end - local v_cur = v + local v_cur = (TYPE)(v) local v_old_ref_for_accum_remainder = 0.0 local logarithmic_zero_epsilon = 0.0 -- Only valid when is_logarithmic is true @@ -2697,10 +2680,10 @@ function ImGui.DragBehaviorT(data_type, v, v_speed, v_min, v_max, format, flags) -- Convert to parametric space, apply delta, convert back local v_old_parametric = ImGui.ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) local v_new_parametric = v_old_parametric + g.DragCurrentAccum - v_cur = ImGui.ScaleValueFromRatioT(data_type, v_new_parametric, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + v_cur = ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, v_new_parametric, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) v_old_ref_for_accum_remainder = v_old_parametric else - v_cur = v_cur + g.DragCurrentAccum + v_cur = (TYPE)(v_cur + (SIGNEDTYPE)(g.DragCurrentAccum)) end -- Round to user desired precision based on format string @@ -2713,27 +2696,32 @@ function ImGui.DragBehaviorT(data_type, v, v_speed, v_min, v_max, format, flags) if is_logarithmic then -- Convert to parametric space, apply delta, convert back local v_new_parametric = ImGui.ScaleRatioFromValueT(data_type, v_cur, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) - g.DragCurrentAccum = v_new_parametric - v_old_ref_for_accum_remainder + g.DragCurrentAccum = g.DragCurrentAccum - (v_new_parametric - v_old_ref_for_accum_remainder) else - g.DragCurrentAccum = v_cur - v + g.DragCurrentAccum = g.DragCurrentAccum - ((SIGNEDTYPE)(v_cur) - (SIGNEDTYPE)(v)) + end + + -- This does nothing + if v_cur == (TYPE)(-0) then + v_cur = (TYPE)(0) end if v ~= v_cur and is_bounded then if is_wrapped then -- Wrap values if v_cur < v_min then - v_cur = v_cur + (v_max - v_min) + (is_floating_point and 0 or 1) + v_cur = (TYPE)(v_cur + (TYPE)((TYPE)(v_max - v_min) + (is_floating_point and 0 or 1))) end if v_cur > v_max then - v_cur = v_cur - (v_max - v_min) - (is_floating_point and 0 or 1) + v_cur = (TYPE)(v_cur - (TYPE)((TYPE)(v_max - v_min) - (is_floating_point and 0 or 1))) end else -- Clamp values + handle overflow/wrap-around for integer types if v_cur < v_min or (v_cur > v and adjust_delta < 0.0 and not is_floating_point) then - v_cur = v_min + v_cur = (TYPE)(v_min) end if v_cur > v_max or (v_cur < v and adjust_delta > 0.0 and not is_floating_point) then - v_cur = v_max + v_cur = (TYPE)(v_max) end end end @@ -2773,21 +2761,21 @@ function ImGui.DragBehavior(id, data_type, v, v_speed, min, max, format, flags) end if data_type == ImGuiDataType.S8 then - return ImGui.DragBehaviorT(ImGuiDataType.S32, v, v_speed, min and min or IM_S8_MIN, max and max or IM_S8_MAX, format, flags) + return ImGui.DragBehaviorT(ImS32, ImS32, ImGuiDataType.S32, v, v_speed, min and min or IM_S8_MIN, max and max or IM_S8_MAX, format, flags) elseif data_type == ImGuiDataType.U8 then - return ImGui.DragBehaviorT(ImGuiDataType.U32, v, v_speed, min and min or IM_U8_MIN, max and max or IM_U8_MAX, format, flags) + return ImGui.DragBehaviorT(ImU32, ImS32, ImGuiDataType.U32, v, v_speed, min and min or IM_U8_MIN, max and max or IM_U8_MAX, format, flags) elseif data_type == ImGuiDataType.S16 then - return ImGui.DragBehaviorT(ImGuiDataType.S32, v, v_speed, min and min or IM_S16_MIN, max and max or IM_S16_MAX, format, flags) + return ImGui.DragBehaviorT(ImS32, ImS32, ImGuiDataType.S32, v, v_speed, min and min or IM_S16_MIN, max and max or IM_S16_MAX, format, flags) elseif data_type == ImGuiDataType.U16 then - return ImGui.DragBehaviorT(ImGuiDataType.U32, v, v_speed, min and min or IM_U16_MIN, max and max or IM_U16_MAX, format, flags) + return ImGui.DragBehaviorT(ImU32, ImS32, ImGuiDataType.U32, v, v_speed, min and min or IM_U16_MIN, max and max or IM_U16_MAX, format, flags) elseif data_type == ImGuiDataType.S32 then - return ImGui.DragBehaviorT(data_type, v, v_speed, min and min or IM_S32_MIN, max and max or IM_S32_MAX, format, flags) + return ImGui.DragBehaviorT(ImS32, ImS32, data_type, v, v_speed, min and min or IM_S32_MIN, max and max or IM_S32_MAX, format, flags) elseif data_type == ImGuiDataType.U32 then - return ImGui.DragBehaviorT(data_type, v, v_speed, min and min or IM_U32_MIN, max and max or IM_U32_MAX, format, flags) + return ImGui.DragBehaviorT(ImU32, ImS32, data_type, v, v_speed, min and min or IM_U32_MIN, max and max or IM_U32_MAX, format, flags) elseif data_type == ImGuiDataType.Float then - return ImGui.DragBehaviorT(data_type, v, v_speed, min and min or -FLT_MAX, max and max or FLT_MAX, format, flags) + return ImGui.DragBehaviorT(float, float, data_type, v, v_speed, min and min or -FLT_MAX, max and max or FLT_MAX, format, flags) elseif data_type == ImGuiDataType.Double then - return ImGui.DragBehaviorT(data_type, v, v_speed, min and min or -DBL_MAX, max and max or DBL_MAX, format, flags) + return ImGui.DragBehaviorT(double, double, data_type, v, v_speed, min and min or -DBL_MAX, max and max or DBL_MAX, format, flags) end IM_ASSERT(false) @@ -3030,14 +3018,15 @@ function ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero end -- Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT) +--- @param TYPE fun(val: number): number +--- @param SIGNEDTYPE fun(val: number): number --- @param data_type ImGuiDataType --- @param t float --- @param v_min number --- @param v_max number --- @param logarithmic_zero_epsilon float --- @param zero_deadzone_halfsize float -function ImGui.ScaleValueFromRatioT(data_type, t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) -DATA_TYPE(data_type) +function ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) -- We special-case the extents because otherwise our logarithmic fudging can lead to "mathematically correct" -- but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler. if t <= 0.0 or v_min == v_max then @@ -3111,6 +3100,8 @@ DATA_TYPE(data_type) end --- @generic T : number +--- @param TYPE fun(val: number): T +--- @param SIGNEDTYPE fun(val: number): T --- @param bb ImRect --- @param id ImGuiID --- @param data_type ImGuiDataType @@ -3122,7 +3113,7 @@ end --- @param out_grab_bb ImRect --- @return T v # updated `v` passed in --- @return bool value_changed -function ImGui.SliderBehaviorT(bb, id, data_type, v, v_min, v_max, format, flags, out_grab_bb) +function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_max, format, flags, out_grab_bb) local g = GImGui local style = g.Style @@ -3221,7 +3212,7 @@ function ImGui.SliderBehaviorT(bb, id, data_type, v, v_min, v_max, format, flags local old_clicked_t = clicked_t clicked_t = ImSaturate(clicked_t + delta) - local v_new = ImGui.ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + local v_new = ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) if is_floating_point and bit.band(flags, ImGuiSliderFlags.NoRoundToFormat) == 0 then v_new = ImGui.RoundScalarWithFormatT(format, data_type, v_new) end @@ -3245,7 +3236,7 @@ function ImGui.SliderBehaviorT(bb, id, data_type, v, v_min, v_max, format, flags end if set_new_value then - local v_new = ImGui.ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) + local v_new = ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, clicked_t, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) if is_floating_point and bit.band(flags, ImGuiSliderFlags.NoRoundToFormat) == 0 then v_new = ImGui.RoundScalarWithFormatT(format, data_type, v_new) @@ -3294,36 +3285,36 @@ function ImGui.SliderBehavior(bb, id, data_type, v, min, max, format, flags, out if data_type == ImGuiDataType.S8 then local v32 = (ImS32)((ImS8)(v)); local r - v32, r = ImGui.SliderBehaviorT(bb, id, ImGuiDataType.S32, v32, min, max, format, flags, out_grab_bb) + v32, r = ImGui.SliderBehaviorT(ImS32, ImS32, bb, id, ImGuiDataType.S32, v32, min, max, format, flags, out_grab_bb) if r then v = (ImS8)(v32) end return v, r elseif data_type == ImGuiDataType.U8 then local v32 = (ImU32)((ImU8)(v)); local r - v32, r = ImGui.SliderBehaviorT(bb, id, ImGuiDataType.U32, v32, min, max, format, flags, out_grab_bb) + v32, r = ImGui.SliderBehaviorT(ImU32, ImS32, bb, id, ImGuiDataType.U32, v32, min, max, format, flags, out_grab_bb) if r then v = (ImU8)(v32) end return v, r elseif data_type == ImGuiDataType.S16 then local v32 = (ImS32)((ImS16)(v)); local r - v32, r = ImGui.SliderBehaviorT(bb, id, ImGuiDataType.S32, v32, min, max, format, flags, out_grab_bb) + v32, r = ImGui.SliderBehaviorT(ImS32, ImS32, bb, id, ImGuiDataType.S32, v32, min, max, format, flags, out_grab_bb) if r then v = (ImS16)(v32) end return v, r elseif data_type == ImGuiDataType.U16 then local v32 = (ImU32)((ImU16)(v)); local r - v32, r = ImGui.SliderBehaviorT(bb, id, ImGuiDataType.U32, v32, min, max, format, flags, out_grab_bb) + v32, r = ImGui.SliderBehaviorT(ImU32, ImS32, bb, id, ImGuiDataType.U32, v32, min, max, format, flags, out_grab_bb) if r then v = (ImU16)(v32) end return v, r elseif data_type == ImGuiDataType.S32 then IM_ASSERT(min >= math.floor(IM_S32_MIN / 2) and max <= math.floor(IM_S32_MAX / 2)) - return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) + return ImGui.SliderBehaviorT(ImS32, ImS32, bb, id, data_type, v, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.U32 then IM_ASSERT(max <= math.floor(IM_U32_MAX / 2)) - return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) + return ImGui.SliderBehaviorT(ImU32, ImS32, bb, id, data_type, v, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.Float then IM_ASSERT(min >= -FLT_MAX / 2.0 and max <= FLT_MAX / 2.0) - return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) + return ImGui.SliderBehaviorT(float, float, bb, id, data_type, v, min, max, format, flags, out_grab_bb) elseif data_type == ImGuiDataType.Double then IM_ASSERT(min >= -DBL_MAX / 2.0 and max <= DBL_MAX / 2.0) - return ImGui.SliderBehaviorT(bb, id, data_type, v, min, max, format, flags, out_grab_bb) + return ImGui.SliderBehaviorT(double, double, bb, id, data_type, v, min, max, format, flags, out_grab_bb) end IM_ASSERT(false) From 491a5e2834e273f31c7f919597bdf52d4bacecc3 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Sat, 13 Jun 2026 12:19:28 +0800 Subject: [PATCH 15/22] Update `DragBehaviorT()` annotations --- lua/imgui_widgets.lua | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 0975e3f..558f2a6 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -2576,17 +2576,18 @@ function ImGui.InputInt(label, v, step, step_fast, flags) end -- This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) ---- @param TYPE function ---- @param SIGNEDTYPE function +--- @generic T : number +--- @param TYPE fun(val: number): T +--- @param SIGNEDTYPE fun(val: number): T --- @param data_type ImGuiDataType ---- @param v number +--- @param v T --- @param v_speed float ---- @param v_min number ---- @param v_max number +--- @param v_min T +--- @param v_max T --- @param format string --- @param flags ImGuiSliderFlags ---- @return number new_v # Updated `v` ---- @return bool changed +--- @return T new_v # Updated `v` +--- @return bool changed function ImGui.DragBehaviorT(TYPE, SIGNEDTYPE, data_type, v, v_speed, v_min, v_max, format, flags) local g = GImGui local axis = (bit.band(flags, ImGuiSliderFlags.Vertical) ~= 0) and ImGuiAxis.Y or ImGuiAxis.X From 6664bff3fd7698a3ca69756fcbc089af727ad4b4 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Sat, 13 Jun 2026 18:26:22 +0800 Subject: [PATCH 16/22] Update `DragFloat()` def, add related demo code --- lua/imgui_demo.lua | 6 ++++++ lua/imgui_widgets.lua | 20 +++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lua/imgui_demo.lua b/lua/imgui_demo.lua index da4c4fe..0219f47 100644 --- a/lua/imgui_demo.lua +++ b/lua/imgui_demo.lua @@ -178,6 +178,9 @@ static.i1 = 50 static.i2 = 42 static.i3 = 128 +static.f1 = 1.00 +static.f2 = 0.0067 + local f1 = 0.123 local f2 = 0.0 @@ -265,6 +268,9 @@ function DemoWindowWidgetsBasic() static.i1 = ImGui.DragInt("drag int", static.i1, 1) static.i2 = ImGui.DragInt("drag int 0..100", static.i2, 1, 0, 100, "%d%%", ImGuiSliderFlags.AlwaysClamp) static.i3 = ImGui.DragInt("drag int wrap 100..200", static.i3, 1, 100, 200, "%d", ImGuiSliderFlags.WrapAround) + + static.f1 = ImGui.DragFloat("drag float", static.f1, 0.005) + static.f2 = ImGui.DragFloat("drag small float", static.f2, 0.0001, 0.0, 0.0, "%.06f ns") end ImGui.SeparatorText("Sliders") diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 558f2a6..211cc50 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -2911,14 +2911,20 @@ function ImGui.DragScalar(label, data_type, data, v_speed, min, max, format, fla return data, value_changed end ---- @param label string ---- @param v float ---- @param v_speed float ---- @param v_min float ---- @param v_max float ---- @param format string ---- @param flags ImGuiSliderFlags +--- @param label string +--- @param v float +--- @param v_speed? float +--- @param v_min? float +--- @param v_max? float +--- @param format? string +--- @param flags? ImGuiSliderFlags function ImGui.DragFloat(label, v, v_speed, v_min, v_max, format, flags) + if v_speed == nil then v_speed = 1.0 end + if v_min == nil then v_min = 0.0 end + if v_max == nil then v_max = 0.0 end + if format == nil then format = "%.3f" end + if flags == nil then flags = 0 end + return ImGui.DragScalar(label, ImGuiDataType.Float, v, v_speed, v_min, v_max, format, flags) end From f65c20c5d656d2b31f67c7c9293e45b0ffd8b693 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Sat, 13 Jun 2026 19:42:17 +0800 Subject: [PATCH 17/22] Attempt to fix `ScaleValueFromRatioT()` bug that caused `SliderInt()` grab to misbehave? --- lua/imgui_widgets.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 211cc50..f211df6 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -3094,12 +3094,8 @@ function ImGui.ScaleValueFromRatioT(TYPE, SIGNEDTYPE, data_type, t, v_min, v_max if is_floating_point then result = ImLerp(v_min, v_max, t) elseif t < 1.0 then - -- - For integer values we want the clicking position to match the grab box so we round above - -- This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. - -- - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64 - -- range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits. local v_new_off_f = (SIGNEDTYPE)((v_max - v_min)) * t - result = (TYPE)((SIGNEDTYPE)(v_min) + (SIGNEDTYPE)(v_new_off_f + ((v_min > v_max) and -0.5 or 0.5))) + result = (TYPE)((SIGNEDTYPE)(v_min) + (SIGNEDTYPE)(v_new_off_f + 0)) -- LUA: + 0 instead of + ((v_min > v_max) and -0.5 or 0.5) end end From 471c65e2307c5c686a2a78711ddbf9f6938dbc23 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Tue, 16 Jun 2026 09:01:34 +0800 Subject: [PATCH 18/22] Partial sync with ocornut/imgui@e5ff2d07d732e6145f5183d5e8116e13c386ab4d and ocornut/imgui@2e0f9490398dd2d78ff4607cde500d581a725516 --- lua/imgui.lua | 2 ++ lua/imgui_internal.lua | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lua/imgui.lua b/lua/imgui.lua index 38bd6c0..18185bd 100644 --- a/lua/imgui.lua +++ b/lua/imgui.lua @@ -863,7 +863,9 @@ local GLocalizationEntriesEnUS = { ImGuiLocEntry(ImGuiLocKey.TableSizeOne, "Size column to fit###SizeOne"), ImGuiLocEntry(ImGuiLocKey.TableSizeAllFit, "Size all columns to fit###SizeAll"), ImGuiLocEntry(ImGuiLocKey.TableSizeAllDefault, "Size all columns to default###SizeAll"), + ImGuiLocEntry(ImGuiLocKey.TableReset, "Reset"), ImGuiLocEntry(ImGuiLocKey.TableResetOrder, "Reset order###ResetOrder"), + ImGuiLocEntry(ImGuiLocKey.TableResetVisibility, "Reset visibility###ResetVisibility"), ImGuiLocEntry(ImGuiLocKey.WindowingMainMenuBar, "(Main menu bar)"), ImGuiLocEntry(ImGuiLocKey.WindowingPopup, "(Popup)"), ImGuiLocEntry(ImGuiLocKey.WindowingUntitled, "(Untitled)"), diff --git a/lua/imgui_internal.lua b/lua/imgui_internal.lua index a67b585..1c6cbdd 100644 --- a/lua/imgui_internal.lua +++ b/lua/imgui_internal.lua @@ -2679,6 +2679,7 @@ ImGuiDebugLogFlags = { EventInputRouting = bit.lshift(1, 9), EventDocking = bit.lshift(1, 10), EventViewport = bit.lshift(1, 11), + EventTable = bit.lshift(1, 12), OutputToTTY = bit.lshift(1, 20), -- Also send output to TTY OutputToDebugger = bit.lshift(1, 21), -- Also send output to Debugger Console @@ -2693,6 +2694,7 @@ ImGuiDebugLogFlags.EventMask_ = bit.bor( ImGuiDebugLogFlags.EventNav, ImGuiDebugLogFlags.EventClipper, ImGuiDebugLogFlags.EventSelection, + ImGuiDebugLogFlags.EventTable, ImGuiDebugLogFlags.EventIO, ImGuiDebugLogFlags.EventFont, ImGuiDebugLogFlags.EventInputRouting, @@ -2706,16 +2708,18 @@ ImGuiLocKey = { TableSizeOne = 2, TableSizeAllFit = 3, TableSizeAllDefault = 4, - TableResetOrder = 5, - WindowingMainMenuBar = 6, - WindowingPopup = 7, - WindowingUntitled = 8, - OpenLink_s = 9, - CopyLink = 10, - DockingHideTabBar = 11, - DockingHoldShiftToDock = 12, - DockingDragToUndockOrMoveNode = 13, - COUNT = 13 + TableReset = 5, + TableResetOrder = 6, + TableResetVisibility = 7, + WindowingMainMenuBar = 8, + WindowingPopup = 9, + WindowingUntitled = 10, + OpenLink_s = 11, + CopyLink = 12, + DockingHideTabBar = 13, + DockingHoldShiftToDock = 14, + DockingDragToUndockOrMoveNode = 15, + COUNT = 15 } --- @class ImGuiLocEntry From 9e6a66426bb6cba63a0315074f71f676315b9eeb Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:00:24 +0800 Subject: [PATCH 19/22] Sync with ocornut/imgui@1ba29f2beccf56b7231ddbeb97dfb2c33b17248a --- lua/imgui.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/imgui.lua b/lua/imgui.lua index 18185bd..363ddc8 100644 --- a/lua/imgui.lua +++ b/lua/imgui.lua @@ -5513,7 +5513,7 @@ function ImGui.Begin(name, open, flags) -- Default item width. Make it proportional to window size if window can be manually resized. -- (we cannot use AutoFitFramesX/AutoFitFramesY which is a temporary state) local is_resizable_width - if bit.band(flags, ImGuiWindowFlags.ChildWindow) ~= 0 then + if bit.band(flags, ImGuiWindowFlags.ChildWindow) ~= 0 and not window.DockIsActive then is_resizable_width = (window.Size.x > 0.0) and (bit.band(window.ChildFlags, bit.bor(ImGuiChildFlags.AutoResizeX, ImGuiChildFlags.AlwaysAutoResize)) == 0) else is_resizable_width = (window.Size.x > 0.0) and (bit.band(flags, ImGuiWindowFlags.AlwaysAutoResize) == 0) From 3330f0ab8af22d3f3642af01f2b2bb4a71006a57 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Wed, 17 Jun 2026 10:59:54 +0800 Subject: [PATCH 20/22] Add `SliderAngle()` --- lua/imgui_demo.lua | 4 ++++ lua/imgui_widgets.lua | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lua/imgui_demo.lua b/lua/imgui_demo.lua index 0219f47..10ec0fd 100644 --- a/lua/imgui_demo.lua +++ b/lua/imgui_demo.lua @@ -1,6 +1,7 @@ --- ImGui Sincerely WIP -- (Demo Code) +-- FIXME: this is problematic local static = {} local IM_MIN = math.min @@ -183,6 +184,7 @@ static.f2 = 0.0067 local f1 = 0.123 local f2 = 0.0 +local angle = 0.0 local Element = { Fire = 1, Earth = 2, Air = 3, Water = 4, COUNT = 5 } local elems_names = { "Fire", "Earth", "Air", "Water" } @@ -279,6 +281,8 @@ function DemoWindowWidgetsBasic() f1 = ImGui.SliderFloat("slider float", f1, 0.0, 1.0, "ratio = %.3f") f2 = ImGui.SliderFloat("slider float (log)", f2, -10.0, 10.0, "%.4f", ImGuiSliderFlags.Logarithmic) + angle = ImGui.SliderAngle("slider angle", angle) + local elem_name = (elem >= 1 and elem < Element.COUNT) and elems_names[elem] or "Unknown" elem = ImGui.SliderInt("slider enum", elem, 1, Element.COUNT - 1, elem_name) end diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index f211df6..1a2e011 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -3441,6 +3441,29 @@ function ImGui.SliderFloat(label, v, v_min, v_max, format, flags) return ImGui.SliderScalar(label, ImGuiDataType.Float, v, v_min, v_max, format, flags) end +--- @param label string +--- @param v_rad float +--- @param v_degrees_min? float +--- @param v_degrees_max? float +--- @param format? string +--- @param flags? ImGuiSliderFlags +--- @return float v_rad # updated `v_rad` +--- @return bool value_changed +function ImGui.SliderAngle(label, v_rad, v_degrees_min, v_degrees_max, format, flags) + if v_degrees_min == nil then v_degrees_min = -360.0 end + if v_degrees_max == nil then v_degrees_max = 360.0 end + if format == nil then format = "%.0f deg" end + if flags == nil then flags = 0 end + + local v_deg = v_rad * 360.0 / (2 * IM_PI) + local value_changed + v_deg, value_changed = ImGui.SliderFloat(label, v_deg, v_degrees_min, v_degrees_max, format, flags) + if value_changed then + v_rad = v_deg * (2 * IM_PI) / 360.0 + end + return v_rad, value_changed +end + --- @param label string --- @param v int --- @param v_min int From d97484c81b2f8a72ba678838cfe858c7f3d4a035 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Fri, 19 Jun 2026 10:38:48 +0800 Subject: [PATCH 21/22] Add currently untested `SliderScalarN()` and `SliderFloat2|3|4()` --- lua/imgui_widgets.lua | 78 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/lua/imgui_widgets.lua b/lua/imgui_widgets.lua index 1a2e011..ddb3df3 100644 --- a/lua/imgui_widgets.lua +++ b/lua/imgui_widgets.lua @@ -3428,6 +3428,84 @@ function ImGui.SliderScalar(label, data_type, data, min, max, format, flags) return data, value_changed end +--- @param label string +--- @param data_type ImGuiDataType +--- @param v table +--- @param components int +--- @param v_min number +--- @param v_max number +--- @param format? string +--- @param flags? ImGuiSliderFlags +function ImGui.SliderScalarN(label, data_type, v, components, v_min, v_max, format, flags) + if flags == nil then flags = 0 end + + local window = ImGui.GetCurrentWindow() + if window.SkipItems then + return false + end + + local g = GImGui + local value_changed = false + ImGui.BeginGroup() + ImGui.PushID(label) + ImGui.PushMultiItemsWidths(components, ImGui.CalcItemWidth()) + + for i = 1, components do + ImGui.PushID(i - 1) + if i > 1 then + ImGui.SameLine(0, g.Style.ItemInnerSpacing.x) + end + if bit.band(flags, ImGuiSliderFlags.ColorMarkers) ~= 0 then + ImGui.SetNextItemColorMarker(GDefaultRgbaColorMarkers[i]) + end + local changed + v[i], changed = ImGui.SliderScalar("", data_type, v[i], v_min, v_max, format, flags) + value_changed = value_changed or changed + ImGui.PopID() + ImGui.PopItemWidth() + end + ImGui.PopID() + + local label_end = ImGui.FindRenderedTextEnd(label) + if label_end > 1 then + ImGui.SameLine(0, g.Style.ItemInnerSpacing.x) + ImGui.TextEx(label, label_end) + end + + ImGui.EndGroup() + return value_changed +end + +--- @param label string +--- @param v [float, float] +--- @param v_min float +--- @param v_max float +--- @param format? string +--- @param flags? ImGuiSliderFlags +function ImGui.SliderFloat2(label, v, v_min, v_max, format, flags) + return ImGui.SliderScalarN(label, ImGuiDataType.Float, v, 2, v_min, v_max, format, flags) +end + +--- @param label string +--- @param v [float, float, float] +--- @param v_min float +--- @param v_max float +--- @param format? string +--- @param flags? ImGuiSliderFlags +function ImGui.SliderFloat3(label, v, v_min, v_max, format, flags) + return ImGui.SliderScalarN(label, ImGuiDataType.Float, v, 3, v_min, v_max, format, flags) +end + +--- @param label string +--- @param v [float, float, float, float] +--- @param v_min float +--- @param v_max float +--- @param format? string +--- @param flags? ImGuiSliderFlags +function ImGui.SliderFloat4(label, v, v_min, v_max, format, flags) + return ImGui.SliderScalarN(label, ImGuiDataType.Float, v, 4, v_min, v_max, format, flags) +end + --- @param label string --- @param v float --- @param v_min float From 58f3758d56291883df926aceb88f76300eeca3f9 Mon Sep 17 00:00:00 2001 From: GrayWolf64 <88234821+GrayWolf64@users.noreply.github.com> Date: Fri, 19 Jun 2026 10:46:02 +0800 Subject: [PATCH 22/22] Mark current `ImStd.sscanf()` impl as *deprecated* The impl can only go way more complex if written without using patterns. (impl means: both the sscanf and future sprintf) --- lua/imgui.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/imgui.lua b/lua/imgui.lua index 363ddc8..4553360 100644 --- a/lua/imgui.lua +++ b/lua/imgui.lua @@ -112,6 +112,7 @@ local IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111 local string = string ImFormatString = string.format -- TODO: an simplified version that operates on byte tables directly? +--- @deprecated --- @module "imstd_minstdio" ImStd.sscanf = IM_INCLUDE"imstd_minstdio.lua"