demo: port all Widgets sections, ShowStyleEditor/ShowStyleSelector, menus; fix crashes found by click fuzzer (color/scalar default args, InputTextWithHint string hint, AddText overload, splitter reset, CollapsingHeader close button, callback data methods, SetKeyboardFocusHere & misc getters)

This commit is contained in:
4DBug
2026-10-08 08:06:00 -05:00
parent 701d1d3449
commit d7a2b8a4df
10 changed files with 4386 additions and 23 deletions
+80
View File
@@ -12930,6 +12930,86 @@ function ImGui.BeginTooltipHidden()
return ret
end
-- [SECTION] MISC GETTERS/SETTERS, FOCUS
function ImGui.NavMoveRequestResolveWithLastItem(result)
local g = GImGui
g.NavMoveScoringItems = false -- Ensure request doesn't need more processing
ImGui.NavApplyItemToResult(result)
ImGui.NavUpdateAnyRequestFlag()
end
function ImGui.SetKeyboardFocusHere(offset)
if offset == nil then offset = 0 end
local g = GImGui
local window = g.CurrentWindow
IM_ASSERT(offset >= -1) -- -1 is allowed but not below
if g.DragDropActive or g.MovingWindow ~= nil then
return
end
ImGui.SetNavWindow(window)
local move_flags = bit32.bor(ImGuiNavMoveFlags.IsTabbing, ImGuiNavMoveFlags.Activate, ImGuiNavMoveFlags.FocusApi, ImGuiNavMoveFlags.NoSetNavCursorVisible)
local scroll_flags = window.Appearing and bit32.bor(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.AlwaysCenterY) or bit32.bor(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.KeepVisibleEdgeY)
ImGui.NavMoveRequestSubmit(ImGuiDir.None, (offset < 0) and ImGuiDir.Up or ImGuiDir.Down, move_flags, scroll_flags)
if offset == -1 then
ImGui.NavMoveRequestResolveWithLastItem(g.NavMoveResultLocal)
else
g.NavTabbingDir = 1
g.NavTabbingCounter = offset + 1
end
end
function ImGui.FocusItem()
local g = GImGui
local window = g.CurrentWindow
if g.DragDropActive or g.MovingWindow ~= nil then
return
end
local move_flags = bit32.bor(ImGuiNavMoveFlags.IsTabbing, ImGuiNavMoveFlags.FocusApi, ImGuiNavMoveFlags.NoSetNavCursorVisible, ImGuiNavMoveFlags.NoSelect)
local scroll_flags = window.Appearing and bit32.bor(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.AlwaysCenterY) or bit32.bor(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.KeepVisibleEdgeY)
ImGui.SetNavWindow(window)
ImGui.NavMoveRequestSubmit(ImGuiDir.None, ImGuiDir.Up, move_flags, scroll_flags)
ImGui.NavMoveRequestResolveWithLastItem(g.NavMoveResultLocal)
end
function ImGui.ActivateItemByID(id)
local g = GImGui
g.NavNextActivateId = id
g.NavNextActivateFlags = ImGuiActivateFlags.None
end
function ImGui.SetNavCursorVisible(visible)
local g = GImGui
if g.NavWindow and bit32.band(g.NavWindow.Flags, ImGuiWindowFlags.NoNavInputs) ~= 0 then
visible = false
elseif g.IO.ConfigNavCursorVisibleAlways then
visible = true
end
g.NavCursorVisible = visible
end
function ImGui.GetFrameCount() return GImGui.FrameCount end
function ImGui.GetStateStorage() return GImGui.CurrentWindow.DC.StateStorage end
function ImGui.SetStateStorage(tree)
local window = GImGui.CurrentWindow
window.DC.StateStorage = tree or window.StateStorage
end
function ImGui.SetNextFrameWantCaptureKeyboard(want) GImGui.WantCaptureKeyboardNextFrame = want and 1 or 0 end
function ImGui.SetNextFrameWantCaptureMouse(want) GImGui.WantCaptureMouseNextFrame = want and 1 or 0 end
function ImGui.GetVersion() return IMGUI_VERSION end
local StyleColorNames = nil
function ImGui.GetStyleColorName(idx)
if StyleColorNames == nil then
StyleColorNames = {}
for k, v in pairs(ImGuiCol) do if k ~= "COUNT" then StyleColorNames[v] = k end end
end
return StyleColorNames[idx] or "Unknown"
end
-- [SECTION] NAV END FRAME
local NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15
+1 -5
View File
@@ -119,11 +119,7 @@ end
--- Closable header: CollapsingHeader(label, &p_open). Returns visible, open.
function DemoCollapsingHeaderClosable(label, open)
if not open then return false, open end
local visible = ImGui.CollapsingHeader(label, open)
-- ponytail: until CollapsingHeader(label, p_open) lands, a boolean 2nd arg is treated as flags=None by the library
if type(visible) == "table" then visible = visible[1] end
return visible, open
return ImGui.CollapsingHeader(label, open)
end
--- ImColor::HSV() as ImVec4
+1 -1
View File
@@ -838,7 +838,7 @@ local function DemoWindowLayout_TextClipping()
else
local clip_rect = ImVec4(p0.x, p0.y, p1.x, p1.y)
draw_list:AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255))
draw_list:AddText(ImGui.GetFont(), ImGui.GetFontSize(), text_pos, IM_COL32_WHITE, text_str, nil, 0.0, clip_rect)
draw_list:AddText(ImGui.GetFont(), ImGui.GetFontSize(), text_pos, IM_COL32_WHITE, text_str, 1, #text_str + 1, 0.0, clip_rect)
end
until true end
end
File diff suppressed because it is too large Load Diff
+11
View File
@@ -3129,6 +3129,12 @@ end
-- TODO:
function MT.ImDrawList:_ResetForNewFrame()
if self._Splitter._Count > 1 then
self._Splitter:Merge(self)
end
self._Splitter._Current = 0 -- [Lua] recover from an error thrown mid-split
self._Splitter._Count = 1
self.CmdBuffer:resize(0)
self.IdxBuffer:resize(0)
self.VtxBuffer:resize(0)
@@ -4205,6 +4211,11 @@ end
--- @param wrap_width float
--- @param cpu_fine_clip_rect? ImVec4
function MT.ImDrawList:AddText(font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect)
-- Overload: AddText(pos, col, text, text_end?)
if type(font) == "table" and type(font[1]) == "number" then
font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect = nil, 0.0, font, font_size, pos, 1, col, 0.0, nil
if text_end == nil then text_end = #text + 1 end
end
if text_begin == nil then text_begin = 1 end
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
+57 -1
View File
@@ -67,6 +67,9 @@ function ImS32(val) return bitAnd(val, 0xFFFFFFFF) end
--- @alias ImDrawIdx unsigned_int
IMGUI_VERSION = "1.93.0 WIP"
IMGUI_VERSION_NUM = 19298
IM_UNICODE_CODEPOINT_INVALID = 0xFFFD
IM_UNICODE_CODEPOINT_MAX = 0xFFFF
@@ -1980,10 +1983,63 @@ ImGuiInputTextFlags = {
--- @class ImGuiInputTextCallbackData
--- @return ImGuiInputTextCallbackData
-- Buf is a 1-based zero-terminated byte table; positions (CursorPos, pos...) are 0-based byte offsets like upstream.
MT.ImGuiInputTextCallbackData = {}
MT.ImGuiInputTextCallbackData.__index = MT.ImGuiInputTextCallbackData
function ImGuiInputTextCallbackData()
return {}
return setmetatable({}, MT.ImGuiInputTextCallbackData)
end
function MT.ImGuiInputTextCallbackData:DeleteChars(pos, bytes_count)
IM_ASSERT(pos + bytes_count <= self.BufTextLen)
local buf = self.Buf
local n = self.BufTextLen - bytes_count - pos + 1 -- includes zero terminator
for i = 1, n do buf[pos + i] = buf[pos + bytes_count + i] end
if self.CursorPos >= pos + bytes_count then
self.CursorPos = self.CursorPos - bytes_count
elseif self.CursorPos >= pos then
self.CursorPos = pos
end
self.SelectionStart = self.CursorPos; self.SelectionEnd = self.CursorPos
self.BufDirty = true
self.BufTextLen = self.BufTextLen - bytes_count
end
--- @param new_text string
function MT.ImGuiInputTextCallbackData:InsertChars(pos, new_text)
local new_text_len = #new_text
if new_text_len == 0 then return end
local is_resizable = bit32.band(self.Flags, ImGuiInputTextFlags.CallbackResize) ~= 0
-- We support partial insertion
local avail = self.BufSize - 1 - self.BufTextLen
if not is_resizable and new_text_len > avail then
new_text_len = avail -- ponytail: truncates by bytes, not to the closest UTF-8 codepoint
end
if new_text_len <= 0 then return end
-- ponytail: CallbackResize buffer growth not supported, text is truncated at BufSize instead
if self.BufTextLen + new_text_len + 1 > self.BufSize then
new_text_len = self.BufSize - 1 - self.BufTextLen
if new_text_len <= 0 then return end
end
local buf = self.Buf
for i = self.BufTextLen, pos + 1, -1 do buf[i + new_text_len] = buf[i] end
for i = 1, new_text_len do buf[pos + i] = string.byte(new_text, i) end
buf[self.BufTextLen + new_text_len + 1] = 0
self.BufDirty = true
self.BufTextLen = self.BufTextLen + new_text_len
if self.CursorPos >= pos then
self.CursorPos = self.CursorPos + new_text_len
end
self.SelectionStart = self.CursorPos; self.SelectionEnd = self.CursorPos
end
function MT.ImGuiInputTextCallbackData:SelectAll() self.SelectionStart = 0; self.SelectionEnd = self.BufTextLen end
function MT.ImGuiInputTextCallbackData:ClearSelection() self.SelectionStart = self.BufTextLen; self.SelectionEnd = self.BufTextLen end
function MT.ImGuiInputTextCallbackData:HasSelection() return self.SelectionStart ~= self.SelectionEnd end
--- @alias ImGuiInputTextCallback fun(data: ImGuiInputTextCallbackData)
--- @alias ImGuiMemAllocFunc fun(T: function, start_idx: int, end_idx: int, userdata: any): table
+53 -4
View File
@@ -2704,6 +2704,7 @@ end
--- @param format? string
--- @param flags? ImGuiInputTextFlags
function ImGui.InputScalarN(label, data_type, data, components, step, step_fast, format, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
@@ -3052,6 +3053,8 @@ end
--- @param format string
--- @param flags ImGuiSliderFlags
function ImGui.DragScalar(label, data_type, data, v_speed, min, max, format, flags)
if v_speed == nil then v_speed = 1.0 end
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return data, false
@@ -3922,6 +3925,7 @@ end
--- @param format? string
--- @param flags? ImGuiSliderFlags
function ImGui.VSliderScalar(label, size, 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
@@ -4776,7 +4780,20 @@ end
--- @param flags ImGuiInputTextFlags
--- @param callback? ImGuiInputTextCallback
--- @param callback_user_data any
local HintBufCache = setmetatable({}, { __mode = "k" })
local function HintToBuf(hint)
local t = HintBufCache[hint]
if t == nil then
t = {}
for i = 1, #hint do t[i] = string.byte(hint, i) end
t[#hint + 1] = 0
HintBufCache[hint] = t
end
return t
end
function ImGui.InputTextEx(label, hint, buf, buf_size, size_arg, flags, callback, callback_user_data)
if type(hint) == "string" then hint = HintToBuf(hint) end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
@@ -5823,6 +5840,7 @@ local fmt_table_float = {
--- @param col float[]
--- @param flags ImGuiColorEditFlags
function ImGui.ColorEdit4(label, col, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
@@ -6074,12 +6092,14 @@ end
--- @param flags? ImGuiColorEditFlags
function ImGui.ColorEdit3(label, col, flags)
if flags == nil then flags = 0 end
if flags == nil then flags = 0 end
return ImGui.ColorEdit4(label, col, bit32.bor(flags, ImGuiColorEditFlags.NoAlpha))
end
-- `col` mutated in place, returns value_changed
function ImGui.ColorPicker3(label, col, flags)
if flags == nil then flags = 0 end
local col4 = { col[1], col[2], col[3], 1.0 }
if not ImGui.ColorPicker4(label, col4, bit32.bor(flags or 0, ImGuiColorEditFlags.NoAlpha)) then
return false
@@ -6122,6 +6142,7 @@ local backup_initial_col = {0, 0, 0, 0}
--- @param flags ImGuiColorEditFlags
--- @param ref_col? float[]
function ImGui.ColorPicker4(label, col, flags, ref_col)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
@@ -7356,17 +7377,45 @@ function ImGui.SetNextItemStorageID(storage_id)
g.NextItemData.StorageId = storage_id
end
-- Overloads: CollapsingHeader(label, flags?) -> is_open
-- CollapsingHeader(label, p_visible: bool, flags?) -> is_open, p_visible
--- @param label string
--- @param flags? ImGuiTreeNodeFlags
function ImGui.CollapsingHeader(label, flags)
function ImGui.CollapsingHeader(label, a, b)
local p_visible, flags
if type(a) == "boolean" then p_visible, flags = a, b else flags = a end
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return false
return false, p_visible
end
if p_visible == false then
return false, p_visible
end
local id = window:GetID(label)
return ImGui.TreeNodeBehavior(id, bit32.bor(flags, ImGuiTreeNodeFlags.CollapsingHeader), label)
flags = bit32.bor(flags, ImGuiTreeNodeFlags.CollapsingHeader)
if p_visible ~= nil then
flags = bit32.bor(flags, ImGuiTreeNodeFlags.AllowOverlap, ImGuiTreeNodeFlags.ClipLabelForTrailingButton)
end
local is_open = ImGui.TreeNodeBehavior(id, flags, label)
if p_visible ~= nil then
-- Create a small overlapping close button
local g = GImGui
local last_item_backup = ImGuiLastItemData()
ImGuiLastItemData_Copy(last_item_backup, g.LastItemData)
local button_size = g.FontSize
local button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size)
local button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y
local close_button_id = ImGui.GetIDWithSeed("#CLOSE", nil, id)
if ImGui.CloseButton(close_button_id, ImVec2(button_x, button_y)) then
p_visible = false
end
ImGuiLastItemData_Copy(g.LastItemData, last_item_backup)
end
return is_open, p_visible
end
----------------------------------------------------------------