WIP: tabs, docking, dragdrop, settings, logging, harness

This commit is contained in:
4DBug
2026-10-08 05:35:25 -05:00
parent 693ced0511
commit fee9a84902
26 changed files with 128896 additions and 1836 deletions
+1728 -53
View File
File diff suppressed because it is too large Load Diff
+753 -836
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+289
View File
@@ -0,0 +1,289 @@
--- ImGui Sincerely
-- Drag and drop: 1:1 port of imgui.cpp [SECTION] DRAG AND DROP (docking branch)
-- Payload `Data` is any Lua value (no copy is made). `data_size` is optional and only kept for API parity.
function ImGui.IsDragDropActive()
return ImGui.GetCurrentContext().DragDropActive
end
function ImGui.ClearDragDrop()
local g = ImGui.GetCurrentContext()
g.DragDropActive = false
ImGuiPayload_Clear(g.DragDropPayload)
g.DragDropAcceptFlagsCurr = ImGuiDragDropFlags.None
g.DragDropAcceptIdCurr = 0
g.DragDropAcceptIdPrev = 0
g.DragDropAcceptIdCurrRectSurface = FLT_MAX
g.DragDropAcceptFrameCount = -1
end
function ImGui.BeginDragDropSource(flags)
if flags == nil then flags = 0 end
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
local mouse_button = ImGuiMouseButton.Left
local source_drag_active = false
local source_id = 0
local source_parent_id = 0
if bit32.band(flags, ImGuiDragDropFlags.SourceExtern) == 0 then
source_id = g.LastItemData.ID
if source_id ~= 0 then
if g.ActiveId ~= source_id then return false end
if g.ActiveIdMouseButton ~= -1 then mouse_button = g.ActiveIdMouseButton end
if g.IO.MouseDown[mouse_button] == false or window.SkipItems then return false end
g.ActiveIdAllowOverlap = false
else
if g.IO.MouseDown[mouse_button] == false or window.SkipItems then return false end
if bit32.band(g.LastItemData.StatusFlags, ImGuiItemStatusFlags.HoveredRect) == 0 and (g.ActiveId == 0 or g.ActiveIdWindow ~= window) then
return false
end
if bit32.band(flags, ImGuiDragDropFlags.SourceAllowNullID) == 0 then
IM_ASSERT(false, "Use ImGuiDragDropFlags.SourceAllowNullID for items without an ID")
return false
end
source_id = window:GetIDFromRectangle(g.LastItemData.Rect)
g.LastItemData.ID = source_id
ImGui.KeepAliveID(source_id)
local is_hovered = ImGui.ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags)
if is_hovered and g.IO.MouseClicked[mouse_button] then
ImGui.SetActiveID(source_id, window)
ImGui.FocusWindow(window)
end
if g.ActiveId == source_id then g.ActiveIdAllowOverlap = is_hovered end
end
if g.ActiveId ~= source_id then return false end
source_parent_id = window.IDStack.Data[window.IDStack.Size]
source_drag_active = ImGui.IsMouseDragging(mouse_button)
ImGui.SetActiveIdUsingAllKeyboardKeys()
else
window = nil
source_id = ImHashStr("#SourceExtern")
source_drag_active = true
mouse_button = g.IO.MouseDown[0] and 0 or -1
ImGui.KeepAliveID(source_id)
ImGui.SetActiveID(source_id, nil)
end
IM_ASSERT(g.DragDropWithinTarget == false)
if not source_drag_active then return false end
if not g.DragDropActive then
IM_ASSERT(source_id ~= 0)
ImGui.ClearDragDrop()
local payload = g.DragDropPayload
payload.SourceId = source_id
payload.SourceParentId = source_parent_id
g.DragDropActive = true
g.DragDropSourceFlags = flags
g.DragDropMouseButton = mouse_button
if payload.SourceId == g.ActiveId then g.ActiveIdNoClearOnFocusLoss = true end
end
g.DragDropSourceFrameCount = g.FrameCount
g.DragDropWithinSource = true
if bit32.band(flags, ImGuiDragDropFlags.SourceNoPreviewTooltip) == 0 then
local ret
if g.DragDropAcceptIdPrev ~= 0 and bit32.band(g.DragDropAcceptFlagsPrev, ImGuiDragDropFlags.AcceptNoPreviewTooltip) ~= 0 then
ret = ImGui.BeginTooltipHidden()
else
ret = ImGui.BeginTooltip()
end
IM_ASSERT(ret)
end
if bit32.band(flags, ImGuiDragDropFlags.SourceNoDisableHover) == 0 and bit32.band(flags, ImGuiDragDropFlags.SourceExtern) == 0 then
g.LastItemData.StatusFlags = bit32.band(g.LastItemData.StatusFlags, bit32.bnot(ImGuiItemStatusFlags.HoveredRect))
end
return true
end
function ImGui.EndDragDropSource()
local g = ImGui.GetCurrentContext()
IM_ASSERT(g.DragDropActive)
IM_ASSERT(g.DragDropWithinSource, "Not after a BeginDragDropSource()?")
if bit32.band(g.DragDropSourceFlags, ImGuiDragDropFlags.SourceNoPreviewTooltip) == 0 then
ImGui.EndTooltip()
end
if g.DragDropPayload.DataFrameCount == -1 then ImGui.ClearDragDrop() end
g.DragDropWithinSource = false
end
--- @param data any # any Lua value
--- @return bool accepted
function ImGui.SetDragDropPayload(type, data, data_size, cond)
local g = ImGui.GetCurrentContext()
local payload = g.DragDropPayload
if cond == nil or cond == 0 then cond = ImGuiCond.Always end
IM_ASSERT(type ~= nil)
IM_ASSERT(#type < 32, "Payload type can be at most 32 characters long")
IM_ASSERT(cond == ImGuiCond.Always or cond == ImGuiCond.Once)
IM_ASSERT(payload.SourceId ~= 0)
if cond == ImGuiCond.Always or payload.DataFrameCount == -1 then
payload.DataType = type
payload.Data = data
payload.DataSize = data_size or ((data ~= nil) and 1 or 0)
end
payload.DataFrameCount = g.FrameCount
return (g.DragDropAcceptFrameCount == g.FrameCount) or (g.DragDropAcceptFrameCount == g.FrameCount - 1)
end
function ImGui.BeginDragDropTargetCustom(bb, id)
local g = ImGui.GetCurrentContext()
if not g.DragDropActive then return false end
local window = g.CurrentWindow
local hovered_window = g.HoveredWindowUnderMovingWindow
if hovered_window == nil or window.RootWindowDockTree ~= hovered_window.RootWindowDockTree then return false end
IM_ASSERT(id ~= 0)
if not ImGui.IsMouseHoveringRect(bb.Min, bb.Max) or (id == g.DragDropPayload.SourceId) then return false end
if window.SkipItems then return false end
IM_ASSERT(g.DragDropWithinTarget == false and g.DragDropWithinSource == false)
g.DragDropTargetRect = ImRect(bb.Min, bb.Max)
g.DragDropTargetClipRect = ImRect(window.ClipRect.Min, window.ClipRect.Max)
g.DragDropTargetId = id
g.DragDropTargetFullViewport = 0
g.DragDropWithinTarget = true
return true
end
function ImGui.BeginDragDropTargetViewport(viewport, p_bb)
local g = ImGui.GetCurrentContext()
if not g.DragDropActive then return false end
local bb = p_bb or viewport:GetWorkRect()
local id = viewport.ID
if g.MouseViewport ~= viewport or not ImGui.IsMouseHoveringRect(bb.Min, bb.Max, false) or (id == g.DragDropPayload.SourceId) then
return false
end
IM_ASSERT(g.DragDropWithinTarget == false and g.DragDropWithinSource == false)
g.DragDropTargetRect = ImRect(bb.Min, bb.Max)
g.DragDropTargetClipRect = ImRect(bb.Min, bb.Max)
g.DragDropTargetId = id
g.DragDropTargetFullViewport = id
g.DragDropWithinTarget = true
return true
end
function ImGui.BeginDragDropTarget()
local g = ImGui.GetCurrentContext()
if not g.DragDropActive then return false end
local window = g.CurrentWindow
if bit32.band(g.LastItemData.StatusFlags, ImGuiItemStatusFlags.HoveredRect) == 0 then return false end
local hovered_window = g.HoveredWindowUnderMovingWindow
if hovered_window == nil or window.RootWindowDockTree ~= hovered_window.RootWindowDockTree or window.SkipItems then return false end
local display_rect = (bit32.band(g.LastItemData.StatusFlags, ImGuiItemStatusFlags.HasDisplayRect) ~= 0) and g.LastItemData.DisplayRect or g.LastItemData.Rect
local id = g.LastItemData.ID
if id == 0 then
id = window:GetIDFromRectangle(display_rect)
ImGui.KeepAliveID(id)
end
if g.DragDropPayload.SourceId == id then return false end
IM_ASSERT(g.DragDropWithinTarget == false and g.DragDropWithinSource == false)
g.DragDropTargetRect = ImRect(display_rect.Min, display_rect.Max)
local clip = (bit32.band(g.LastItemData.StatusFlags, ImGuiItemStatusFlags.HasClipRect) ~= 0) and g.LastItemData.ClipRect or window.ClipRect
g.DragDropTargetClipRect = ImRect(clip.Min, clip.Max)
g.DragDropTargetId = id
g.DragDropWithinTarget = true
return true
end
function ImGui.IsDragDropPayloadBeingAccepted()
local g = ImGui.GetCurrentContext()
return g.DragDropActive and g.DragDropAcceptIdPrev ~= 0
end
--- @return ImGuiPayload?
function ImGui.AcceptDragDropPayload(type, flags)
if flags == nil then flags = 0 end
local g = ImGui.GetCurrentContext()
local payload = g.DragDropPayload
IM_ASSERT(g.DragDropActive)
IM_ASSERT(payload.DataFrameCount ~= -1)
if type ~= nil and not ImGuiPayload_IsDataType(payload, type) then return nil end
local was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId)
local r = g.DragDropTargetRect
local r_surface = r:GetWidth() * r:GetHeight()
if r_surface > g.DragDropAcceptIdCurrRectSurface then return nil end
g.DragDropAcceptFlagsCurr = flags
g.DragDropAcceptIdCurr = g.DragDropTargetId
g.DragDropAcceptIdCurrRectSurface = r_surface
payload.Preview = was_accepted_previously
flags = bit32.bor(flags, bit32.band(g.DragDropSourceFlags, ImGuiDragDropFlags.AcceptNoDrawDefaultRect))
local draw_target_rect = payload.Preview and bit32.band(flags, ImGuiDragDropFlags.AcceptNoDrawDefaultRect) == 0
if draw_target_rect and g.DragDropTargetFullViewport ~= 0 then
ImGui.RenderDragDropTargetRectForViewport(g.DragDropTargetFullViewport, g.DragDropTargetRect)
elseif draw_target_rect then
ImGui.RenderDragDropTargetRectForItem(r)
end
g.DragDropAcceptFrameCount = g.FrameCount
if bit32.band(g.DragDropSourceFlags, ImGuiDragDropFlags.SourceExtern) ~= 0 and g.DragDropMouseButton == -1 then
payload.Delivery = was_accepted_previously and (g.DragDropSourceFrameCount < g.FrameCount)
else
payload.Delivery = was_accepted_previously and not ImGui.IsMouseDown(g.DragDropMouseButton)
end
if not payload.Delivery and bit32.band(flags, ImGuiDragDropFlags.AcceptBeforeDelivery) == 0 then return nil end
return payload
end
function ImGui.RenderDragDropTargetRectForItem(bb)
local g = ImGui.GetCurrentContext()
local window = g.CurrentWindow
local bb_display = ImRect(bb.Min, bb.Max)
bb_display:ClipWith(g.DragDropTargetClipRect)
bb_display:Expand(g.Style.DragDropTargetPadding)
local push_clip_rect = not window.ClipRect:Contains(bb_display)
if push_clip_rect then window.DrawList:PushClipRectFullScreen() end
ImGui.RenderDragDropTargetRectEx(window.DrawList, bb_display, g.Style.DragDropTargetRounding)
if push_clip_rect then window.DrawList:PopClipRect() end
end
function ImGui.RenderDragDropTargetRectForViewport(viewport_id, bb)
local g = ImGui.GetCurrentContext()
local viewport = ImGui.FindViewportByID(viewport_id)
IM_ASSERT(viewport ~= nil)
local bb_padded = ImRect(bb.Min, bb.Max)
bb_padded:Expand(-g.Style.DragDropTargetPadding)
ImGui.RenderDragDropTargetRectEx(ImGui.GetForegroundDrawList(viewport), bb_padded, g.Style.DragDropTargetRounding)
end
function ImGui.RenderDragDropTargetRectEx(draw_list, bb, rounding)
local g = ImGui.GetCurrentContext()
draw_list:AddRectFilled(bb.Min, bb.Max, ImGui.GetColorU32(ImGuiCol.DragDropTargetBg), rounding, 0)
draw_list:AddRect(bb.Min, bb.Max, ImGui.GetColorU32(ImGuiCol.DragDropTarget), rounding, 0, g.Style.DragDropTargetBorderSize)
end
--- @return ImGuiPayload?
function ImGui.GetDragDropPayload()
local g = ImGui.GetCurrentContext()
return (g.DragDropActive and g.DragDropPayload.DataFrameCount ~= -1) and g.DragDropPayload or nil
end
function ImGui.EndDragDropTarget()
local g = ImGui.GetCurrentContext()
IM_ASSERT(g.DragDropActive)
IM_ASSERT(g.DragDropWithinTarget)
g.DragDropWithinTarget = false
if g.DragDropPayload.Delivery then ImGui.ClearDragDrop() end
end
-- Payload methods (C++ ImGuiPayload members): payload:IsDataType(t) etc. also work through these globals
function ImGui.PayloadIsDataType(payload, t) return ImGuiPayload_IsDataType(payload, t) end
return true
+218 -1
View File
@@ -137,6 +137,8 @@ function ImGui.StyleColorsDark(dst)
colors[ImGuiCol.TabDimmed] = ImLerpV4V4F(colors[ImGuiCol.Tab], colors[ImGuiCol.TitleBg], 0.80)
colors[ImGuiCol.TabDimmedSelected] = ImLerpV4V4F(colors[ImGuiCol.TabSelected], colors[ImGuiCol.TitleBg], 0.40)
colors[ImGuiCol.TabDimmedSelectedOverline] = ImVec4(0.50, 0.50, 0.50, 0.00)
colors[ImGuiCol.DockingPreview] = ImVec4(colors[ImGuiCol.HeaderActive].x, colors[ImGuiCol.HeaderActive].y, colors[ImGuiCol.HeaderActive].z, colors[ImGuiCol.HeaderActive].w * 0.7)
colors[ImGuiCol.DockingEmptyBg] = ImVec4(0.20, 0.20, 0.20, 1.00)
colors[ImGuiCol.PlotLines] = ImVec4(0.61, 0.61, 0.61, 1.00)
colors[ImGuiCol.PlotLinesHovered] = ImVec4(1.00, 0.43, 0.35, 1.00)
colors[ImGuiCol.PlotHistogram] = ImVec4(0.90, 0.70, 0.00, 1.00)
@@ -205,6 +207,8 @@ function ImGui.StyleColorsClassic(dst)
colors[ImGuiCol.TabDimmed] = ImLerpV4V4F(colors[ImGuiCol.Tab], colors[ImGuiCol.TitleBg], 0.80)
colors[ImGuiCol.TabDimmedSelected] = ImLerpV4V4F(colors[ImGuiCol.TabSelected], colors[ImGuiCol.TitleBg], 0.40)
colors[ImGuiCol.TabDimmedSelectedOverline] = ImVec4(0.53, 0.53, 0.87, 0.00)
colors[ImGuiCol.DockingPreview] = ImVec4(colors[ImGuiCol.Header].x, colors[ImGuiCol.Header].y, colors[ImGuiCol.Header].z, colors[ImGuiCol.Header].w * 0.7)
colors[ImGuiCol.DockingEmptyBg] = ImVec4(0.20, 0.20, 0.20, 1.00)
colors[ImGuiCol.PlotLines] = ImVec4(1.00, 1.00, 1.00, 1.00)
colors[ImGuiCol.PlotLinesHovered] = ImVec4(0.90, 0.70, 0.00, 1.00)
colors[ImGuiCol.PlotHistogram] = ImVec4(0.90, 0.70, 0.00, 1.00)
@@ -274,6 +278,8 @@ function ImGui.StyleColorsLight(dst)
colors[ImGuiCol.TabDimmed] = ImLerpV4V4F(colors[ImGuiCol.Tab], colors[ImGuiCol.TitleBg], 0.80)
colors[ImGuiCol.TabDimmedSelected] = ImLerpV4V4F(colors[ImGuiCol.TabSelected], colors[ImGuiCol.TitleBg], 0.40)
colors[ImGuiCol.TabDimmedSelectedOverline] = ImVec4(0.26, 0.59, 1.00, 0.00)
colors[ImGuiCol.DockingPreview] = ImVec4(colors[ImGuiCol.Header].x, colors[ImGuiCol.Header].y, colors[ImGuiCol.Header].z, colors[ImGuiCol.Header].w * 0.7)
colors[ImGuiCol.DockingEmptyBg] = ImVec4(0.68, 0.69, 0.73, 1.00)
colors[ImGuiCol.PlotLines] = ImVec4(0.39, 0.39, 0.39, 1.00)
colors[ImGuiCol.PlotLinesHovered] = ImVec4(1.00, 0.43, 0.35, 1.00)
colors[ImGuiCol.PlotHistogram] = ImVec4(0.90, 0.70, 0.00, 1.00)
@@ -328,6 +334,18 @@ function ImGui.ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx,
end
end
-- Rotate and translate vertices [vert_start_idx, vert_end_idx) (1-based, like _VtxCurrentIdx)
function ImGui.ShadeVertsTransformPos(draw_list, vert_start_idx, vert_end_idx, pivot_in, cos_a, sin_a, pivot_out)
local data = draw_list.VtxBuffer.Data
local pix, piy, pox, poy = pivot_in.x, pivot_in.y, pivot_out.x, pivot_out.y
for i = vert_start_idx, vert_end_idx - 1 do
local pos = data[i][1]
local x, y = pos[1] - pix, pos[2] - piy
pos[1] = x * cos_a - y * sin_a + pox
pos[2] = x * sin_a + y * cos_a + poy
end
end
-- Distribute UV over (a, b) rectangle
--- @param draw_list ImDrawList
--- @param vert_start_idx int
@@ -3145,7 +3163,7 @@ function MT.ImDrawList:_ClearFreeMemory()
self._TextureStack:clear()
-- self._CallbacksDataBuf:clear()
self._Path:clear()
-- self._Splitter.ClearFreeMemory()
self._Splitter:ClearFreeMemory()
end
function MT.ImDrawList:AddDrawCmd()
@@ -3312,6 +3330,184 @@ function MT.ImDrawList:_OnChangedVtxOffset()
curr_cmd.VtxOffset = self._CmdHeader.VtxOffset
end
----------------------------------------------------------------
-- [SECTION] ImDrawListSplitter
----------------------------------------------------------------
-- Lua: channels swap ImVector objects with the draw list (C++ memcpy of the vector headers).
-- Channel 0 holds nothing while it is the current channel (its buffers live in the draw list).
-- Copy ClipRect, TexRef, VtxOffset (TexRef is assigned by reference, never mutated)
local function ImDrawCmd_HeaderCopyRef(dst, src)
ImVec4_Copy(dst.ClipRect, src.ClipRect)
dst.TexRef = src.TexRef
dst.VtxOffset = src.VtxOffset
end
function MT.ImDrawListSplitter:Clear()
self._Current = 0
self._Count = 1
end
function MT.ImDrawListSplitter:ClearFreeMemory()
for i = 1, self._Channels.Size do
local ch = self._Channels.Data[i]
if i - 1 == self._Current then
-- Current channel is a copy of CmdBuffer/IdxBuffer, don't destruct again
self._Channels.Data[i] = ImDrawChannel()
else
ch._CmdBuffer:clear()
ch._IdxBuffer:clear()
end
end
self._Current = 0
self._Count = 1
self._Channels:clear()
end
--- @param draw_list ImDrawList
--- @param channels_count int
function MT.ImDrawListSplitter:Split(draw_list, channels_count)
IM_ASSERT(self._Current == 0 and self._Count <= 1, "Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter.")
local old_channels_count = self._Channels.Size
if old_channels_count < channels_count then
self._Channels:reserve(channels_count)
self._Channels:resize(channels_count)
end
self._Count = channels_count
-- Channel 0 is the draw list itself (memset to 0 in C++)
local channels = self._Channels.Data
channels[1] = { _CmdBuffer = nil, _IdxBuffer = nil }
for i = 2, channels_count do
if i > old_channels_count or channels[i] == nil then
channels[i] = ImDrawChannel()
else
channels[i]._CmdBuffer:resize(0)
channels[i]._IdxBuffer:resize(0)
end
end
end
--- @param draw_list ImDrawList
function MT.ImDrawListSplitter:Merge(draw_list)
-- Note that we never use or rely on _Channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.
if self._Count <= 1 then
return
end
self:SetCurrentChannel(draw_list, 0)
draw_list:_PopUnusedDrawCmd()
-- Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command.
local new_cmd_buffer_count = 0
local new_idx_buffer_count = 0
local last_cmd = (self._Count > 0 and draw_list.CmdBuffer.Size > 0) and draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size] or nil
local idx_offset = last_cmd and (last_cmd.IdxOffset + last_cmd.ElemCount) or 0
local channels = self._Channels.Data
for i = 2, self._Count do
local ch = channels[i]
local cmds = ch._CmdBuffer
if cmds.Size > 0 and cmds.Data[cmds.Size].ElemCount == 0 and cmds.Data[cmds.Size].UserCallback == nil then -- Equivalent of PopUnusedDrawCmd()
cmds:pop_back()
end
if cmds.Size > 0 and last_cmd ~= nil then
-- Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves.
-- Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter.
local next_cmd = cmds.Data[1]
if ImDrawCmd_HeaderCompare(last_cmd, next_cmd) and last_cmd.UserCallback == nil and next_cmd.UserCallback == nil then
-- Merge previous channel last draw command with current channel first draw command if matching.
last_cmd.ElemCount = last_cmd.ElemCount + next_cmd.ElemCount
idx_offset = idx_offset + next_cmd.ElemCount
cmds:erase(1) -- FIXME-OPT: Improve for multiple merges.
end
end
if cmds.Size > 0 then
last_cmd = cmds.Data[cmds.Size]
end
new_cmd_buffer_count = new_cmd_buffer_count + cmds.Size
new_idx_buffer_count = new_idx_buffer_count + ch._IdxBuffer.Size
for cmd_n = 1, cmds.Size do
cmds.Data[cmd_n].IdxOffset = idx_offset
idx_offset = idx_offset + cmds.Data[cmd_n].ElemCount
end
end
local cmd_write = draw_list.CmdBuffer.Size + 1
local idx_write = draw_list.IdxBuffer.Size + 1
draw_list.CmdBuffer:resize(draw_list.CmdBuffer.Size + new_cmd_buffer_count)
draw_list.IdxBuffer:resize(draw_list.IdxBuffer.Size + new_idx_buffer_count)
-- Write commands and indices in order (they are fairly small structures, we don't copy vertices only indices)
local dst_cmds = draw_list.CmdBuffer.Data
local dst_idx = draw_list.IdxBuffer.Data
for i = 2, self._Count do
local ch = channels[i]
local src_cmds = ch._CmdBuffer.Data
for n = 1, ch._CmdBuffer.Size do
dst_cmds[cmd_write] = src_cmds[n]
src_cmds[n] = nil -- ownership moved to the draw list
cmd_write = cmd_write + 1
end
local src_idx = ch._IdxBuffer.Data
for n = 1, ch._IdxBuffer.Size do
dst_idx[idx_write] = src_idx[n]
idx_write = idx_write + 1
end
end
draw_list._IdxWritePtr = idx_write
-- Ensure there's always a non-callback draw command trailing the command-buffer
if draw_list.CmdBuffer.Size == 0 or draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size].UserCallback ~= nil then
draw_list:AddDrawCmd()
end
-- If current command is used with different settings we need to add a new command
local curr_cmd = draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size]
if curr_cmd.ElemCount == 0 then
ImDrawCmd_HeaderCopyRef(curr_cmd, draw_list._CmdHeader) -- Copy ClipRect, TexRef, VtxOffset
elseif not ImDrawCmd_HeaderCompare(curr_cmd, draw_list._CmdHeader) then
draw_list:AddDrawCmd()
end
self._Count = 1
end
--- @param draw_list ImDrawList
--- @param idx int # 0-based channel index
function MT.ImDrawListSplitter:SetCurrentChannel(draw_list, idx)
IM_ASSERT(idx >= 0 and idx < self._Count)
if self._Current == idx then
return
end
-- Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap()
local channels = self._Channels.Data
local cur = channels[self._Current + 1]
cur._CmdBuffer = draw_list.CmdBuffer
cur._IdxBuffer = draw_list.IdxBuffer
self._Current = idx
local ch = channels[idx + 1]
draw_list.CmdBuffer = ch._CmdBuffer
draw_list.IdxBuffer = ch._IdxBuffer
draw_list._IdxWritePtr = draw_list.IdxBuffer.Size + 1
-- If current command is used with different settings we need to add a new command
local curr_cmd = (draw_list.CmdBuffer.Size == 0) and nil or draw_list.CmdBuffer.Data[draw_list.CmdBuffer.Size]
if curr_cmd == nil then
draw_list:AddDrawCmd()
elseif curr_cmd.ElemCount == 0 then
ImDrawCmd_HeaderCopyRef(curr_cmd, draw_list._CmdHeader) -- Copy ClipRect, TexRef, VtxOffset
elseif not ImDrawCmd_HeaderCompare(curr_cmd, draw_list._CmdHeader) then
draw_list:AddDrawCmd()
end
end
--- @param count int
function MT.ImDrawList:ChannelsSplit(count) self._Splitter:Split(self, count) end
function MT.ImDrawList:ChannelsMerge() self._Splitter:Merge(self) end
--- @param n int # 0-based
function MT.ImDrawList:ChannelsSetCurrent(n) self._Splitter:SetCurrentChannel(self, n) end
--- @param points ImVec2[]
--- @param points_count int
--- @param col ImU32
@@ -3912,6 +4108,27 @@ function MT.ImDrawList:AddTriangle(p1, p2, p3, col, thickness)
self:PathStroke(col, thickness, ImDrawFlags.Closed)
end
function MT.ImDrawList:AddQuad(p1, p2, p3, p4, col, thickness)
if thickness == nil then thickness = 1.0 end
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
self:PathLineTo(p1)
self:PathLineTo(p2)
self:PathLineTo(p3)
self:PathLineTo(p4)
self:PathStroke(col, thickness, ImDrawFlags.Closed)
end
function MT.ImDrawList:AddQuadFilled(p1, p2, p3, p4, col)
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
self:PathLineTo(p1)
self:PathLineTo(p2)
self:PathLineTo(p3)
self:PathLineTo(p4)
self:PathFillConvex(col)
end
function MT.ImDrawList:AddTriangleFilled(p1, p2, p3, col)
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
+175 -23
View File
@@ -513,7 +513,9 @@ function ImDrawChannel() return { _CmdBuffer = ImVector(), _IdxBuffer = ImVector
--- @return ImDrawListSplitter
--- @nodiscard
function ImDrawListSplitter() return { _Current = 0, _Count = 0, _Channels = ImVector() } end
MT.ImDrawListSplitter = {} -- methods in imgui_draw.lua
MT.ImDrawListSplitter.__index = MT.ImDrawListSplitter
function ImDrawListSplitter() return setmetatable({ _Current = 0, _Count = 0, _Channels = ImVector() }, MT.ImDrawListSplitter) end
--- @class ImDrawList
--- @field CmdBuffer ImVector<ImDrawCmd>
@@ -1000,6 +1002,7 @@ ImGuiConfigFlags = {
NoMouse = bitLShift(1, 4),
NoMouseCursorChange = bitLShift(1, 5),
NoKeyboard = bitLShift(1, 6),
DockingEnable = bitLShift(1, 7),
ViewportsEnable = bitLShift(1, 10),
IsSRGB = bitLShift(1, 20),
IsTouchScreen = bitLShift(1, 21)
@@ -1054,7 +1057,25 @@ function ImGuiIO()
ConfigWindowsResizeFromEdges = true,
ConfigDebugIniSettings = false,
ConfigWindowsCopyContentsWithCtrlC = false,
ConfigWindowsMoveFromTitleBarOnly = false,
ConfigErrorRecovery = true,
ConfigErrorRecoveryEnableAssert = true,
ConfigErrorRecoveryEnableDebugLog = true,
ConfigErrorRecoveryEnableTooltip = true,
ConfigDebugHighlightIdConflicts = false,
ConfigDebugHighlightIdConflictsShowItemPicker = true,
IniFilename = "imgui.ini", -- Roblox: no disk access, only used as a flag (nil disables .ini handling)
IniSavingRate = 5.0,
LogFilename = "imgui_log.txt",
WantSaveIniSettings = false,
ConfigDockingNoSplit = false,
ConfigDockingNoDockingOver = false,
ConfigDockingWithShift = false,
ConfigDockingAlwaysTabBar = false,
ConfigDockingTransparentPayload = false,
ConfigViewportsNoAutoMerge = false,
ConfigViewportsNoTaskBarIcon = false,
ConfigViewportsNoDecoration = true,
@@ -1084,6 +1105,7 @@ function ImGuiIO()
MouseDoubleClicked = {[0] = false, [1] = false, [2] = false},
MouseDoubleClickTime = 0.30,
MouseSingleClickDelay = 0.50,
MouseDoubleClickMaxDist = 6.0,
MouseDragThreshold = 6.0,
KeyRepeatDelay = 0.275,
@@ -1546,6 +1568,7 @@ ImGuiHoveredFlags = {
RootWindow = bitLShift(1, 1),
AnyWindow = bitLShift(1, 2),
NoPopupHierarchy = bitLShift(1, 3),
DockHierarchy = bitLShift(1, 4),
AllowWhenBlockedByPopup = bitLShift(1, 5),
AllowWhenBlockedByActiveItem = bitLShift(1, 7),
AllowWhenOverlappedByItem = bitLShift(1, 8),
@@ -1560,11 +1583,22 @@ ImGuiHoveredFlags = {
NoSharedDelay = bitLShift(1, 17)
}
--- @enum ImGuiFocusedFlags
ImGuiFocusedFlags = {
None = 0,
ChildWindows = bitLShift(1, 0), -- Return true if any children of the window is focused
RootWindow = bitLShift(1, 1), -- Test from root window (top most parent of the current hierarchy)
AnyWindow = bitLShift(1, 2), -- Return true if any window is focused
NoPopupHierarchy = bitLShift(1, 3), -- Do not consider popup hierarchy
DockHierarchy = bitLShift(1, 4), -- Consider docking hierarchy
}
ImGuiFocusedFlags.RootAndChildWindows = bitOr(ImGuiFocusedFlags.RootWindow, ImGuiFocusedFlags.ChildWindows)
ImGuiHoveredFlags.AllowWhenOverlapped = bitOr(ImGuiHoveredFlags.AllowWhenOverlappedByItem, ImGuiHoveredFlags.AllowWhenOverlappedByWindow)
ImGuiHoveredFlags.RectOnly = bitOr(ImGuiHoveredFlags.AllowWhenBlockedByPopup, ImGuiHoveredFlags.AllowWhenBlockedByActiveItem, ImGuiHoveredFlags.AllowWhenOverlapped)
ImGuiHoveredFlags.RootAndChildWindows = bitOr(ImGuiHoveredFlags.RootWindow, ImGuiHoveredFlags.ChildWindows)
ImGuiHoveredFlags.DelayMask_ = bitOr(ImGuiHoveredFlags.DelayNone, ImGuiHoveredFlags.DelayShort, ImGuiHoveredFlags.DelayNormal, ImGuiHoveredFlags.NoSharedDelay)
ImGuiHoveredFlags.AllowedMaskForIsWindowHovered = bitOr(ImGuiHoveredFlags.ChildWindows, ImGuiHoveredFlags.RootWindow, ImGuiHoveredFlags.AnyWindow, ImGuiHoveredFlags.NoPopupHierarchy, ImGuiHoveredFlags.AllowWhenBlockedByPopup, ImGuiHoveredFlags.AllowWhenBlockedByActiveItem, ImGuiHoveredFlags.ForTooltip, ImGuiHoveredFlags.Stationary)
ImGuiHoveredFlags.AllowedMaskForIsWindowHovered = bitOr(ImGuiHoveredFlags.ChildWindows, ImGuiHoveredFlags.RootWindow, ImGuiHoveredFlags.AnyWindow, ImGuiHoveredFlags.NoPopupHierarchy, ImGuiHoveredFlags.DockHierarchy, ImGuiHoveredFlags.AllowWhenBlockedByPopup, ImGuiHoveredFlags.AllowWhenBlockedByActiveItem, ImGuiHoveredFlags.ForTooltip, ImGuiHoveredFlags.Stationary)
ImGuiHoveredFlags.AllowedMaskForIsItemHovered = bitOr(ImGuiHoveredFlags.AllowWhenBlockedByPopup, ImGuiHoveredFlags.AllowWhenBlockedByActiveItem, ImGuiHoveredFlags.AllowWhenOverlapped, ImGuiHoveredFlags.AllowWhenDisabled, ImGuiHoveredFlags.NoNavOverride, ImGuiHoveredFlags.ForTooltip, ImGuiHoveredFlags.Stationary, ImGuiHoveredFlags.DelayMask_)
--- @enum ImGuiKey
@@ -1683,26 +1717,28 @@ ImGuiCol = {
TabDimmed = 39,
TabDimmedSelected = 40,
TabDimmedSelectedOverline = 41,
PlotLines = 42,
PlotLinesHovered = 43,
PlotHistogram = 44,
PlotHistogramHovered = 45,
TableHeaderBg = 46,
TableBorderStrong = 47,
TableBorderLight = 48,
TableRowBg = 49,
TableRowBgAlt = 50,
TextLink = 51,
TextSelectedBg = 52,
TreeLines = 53,
DragDropTarget = 54,
DragDropTargetBg = 55,
UnsavedMarker = 56,
NavCursor = 57,
NavWindowingHighlight = 58,
NavWindowingDimBg = 59,
ModalWindowDimBg = 60,
COUNT = 61
DockingPreview = 42,
DockingEmptyBg = 43,
PlotLines = 44,
PlotLinesHovered = 45,
PlotHistogram = 46,
PlotHistogramHovered = 47,
TableHeaderBg = 48,
TableBorderStrong = 49,
TableBorderLight = 50,
TableRowBg = 51,
TableRowBgAlt = 52,
TextLink = 53,
TextSelectedBg = 54,
TreeLines = 55,
DragDropTarget = 56,
DragDropTargetBg = 57,
UnsavedMarker = 58,
NavCursor = 59,
NavWindowingHighlight = 60,
NavWindowingDimBg = 61,
ModalWindowDimBg = 62,
COUNT = 63
}
--- @enum ImGuiBackendFlags
@@ -1992,8 +2028,124 @@ ImGuiTreeNodeFlags.DrawLinesMask_ = bitOr(ImGuiTreeNodeFlags.DrawLinesNone, ImGu
--- @enum ImGuiTableFlags
ImGuiTableFlags =
{
None = 0,
None = 0,
Resizable = bitLShift(1, 0),
Reorderable = bitLShift(1, 1),
Hideable = bitLShift(1, 2),
Sortable = bitLShift(1, 3),
NoSavedSettings = bitLShift(1, 4),
ContextMenuInBody = bitLShift(1, 5),
RowBg = bitLShift(1, 6),
BordersInnerH = bitLShift(1, 7),
BordersOuterH = bitLShift(1, 8),
BordersInnerV = bitLShift(1, 9),
BordersOuterV = bitLShift(1, 10),
NoBordersInBody = bitLShift(1, 11),
NoBordersInBodyUntilResize = bitLShift(1, 12),
SizingFixedFit = bitLShift(1, 13),
SizingFixedSame = bitLShift(2, 13),
SizingStretchProp = bitLShift(3, 13),
SizingStretchSame = bitLShift(4, 13),
NoHostExtendX = bitLShift(1, 16),
NoHostExtendY = bitLShift(1, 17),
NoKeepColumnsVisible = bitLShift(1, 18),
PreciseWidths = bitLShift(1, 19),
NoClip = bitLShift(1, 20),
PadOuterX = bitLShift(1, 21),
NoPadOuterX = bitLShift(1, 22),
NoPadInnerX = bitLShift(1, 23),
ScrollX = bitLShift(1, 24),
ScrollY = bitLShift(1, 25),
SortMulti = bitLShift(1, 26),
SortTristate = bitLShift(1, 27),
HighlightHoveredColumn = bitLShift(1, 28),
}
ImGuiTableFlags.BordersH = bitOr(ImGuiTableFlags.BordersInnerH, ImGuiTableFlags.BordersOuterH)
ImGuiTableFlags.BordersV = bitOr(ImGuiTableFlags.BordersInnerV, ImGuiTableFlags.BordersOuterV)
ImGuiTableFlags.BordersInner = bitOr(ImGuiTableFlags.BordersInnerV, ImGuiTableFlags.BordersInnerH)
ImGuiTableFlags.BordersOuter = bitOr(ImGuiTableFlags.BordersOuterV, ImGuiTableFlags.BordersOuterH)
ImGuiTableFlags.Borders = bitOr(ImGuiTableFlags.BordersInner, ImGuiTableFlags.BordersOuter)
ImGuiTableFlags.SizingMask_ = bitOr(ImGuiTableFlags.SizingFixedFit, ImGuiTableFlags.SizingFixedSame, ImGuiTableFlags.SizingStretchProp, ImGuiTableFlags.SizingStretchSame)
--- @enum ImGuiTableColumnFlags
ImGuiTableColumnFlags =
{
None = 0,
Disabled = bitLShift(1, 0),
DefaultHide = bitLShift(1, 1),
DefaultSort = bitLShift(1, 2),
WidthStretch = bitLShift(1, 3),
WidthFixed = bitLShift(1, 4),
NoResize = bitLShift(1, 5),
NoReorder = bitLShift(1, 6),
NoHide = bitLShift(1, 7),
NoClip = bitLShift(1, 8),
NoSort = bitLShift(1, 9),
NoSortAscending = bitLShift(1, 10),
NoSortDescending = bitLShift(1, 11),
NoHeaderLabel = bitLShift(1, 12),
NoHeaderWidth = bitLShift(1, 13),
PreferSortAscending = bitLShift(1, 14),
PreferSortDescending = bitLShift(1, 15),
IndentEnable = bitLShift(1, 16),
IndentDisable = bitLShift(1, 17),
AngledHeader = bitLShift(1, 18),
IsEnabled = bitLShift(1, 24),
IsVisible = bitLShift(1, 25),
IsSorted = bitLShift(1, 26),
IsHovered = bitLShift(1, 27),
NoDirectResize_ = bitLShift(1, 30),
}
ImGuiTableColumnFlags.WidthMask_ = bitOr(ImGuiTableColumnFlags.WidthStretch, ImGuiTableColumnFlags.WidthFixed)
ImGuiTableColumnFlags.IndentMask_ = bitOr(ImGuiTableColumnFlags.IndentEnable, ImGuiTableColumnFlags.IndentDisable)
ImGuiTableColumnFlags.StatusMask_ = bitOr(ImGuiTableColumnFlags.IsEnabled, ImGuiTableColumnFlags.IsVisible, ImGuiTableColumnFlags.IsSorted, ImGuiTableColumnFlags.IsHovered)
--- @enum ImGuiTableRowFlags
ImGuiTableRowFlags = { None = 0, Headers = bitLShift(1, 0) }
--- @enum ImGuiTableBgTarget
ImGuiTableBgTarget = { None = 0, RowBg0 = 1, RowBg1 = 2, CellBg = 3 }
--- @enum ImGuiSortDirection
ImGuiSortDirection = { None = 0, Ascending = 1, Descending = 2 }
--- @class ImGuiPayload
--- Drag and drop payload. `Data` is any Lua value (C++ stores raw bytes); `DataSize` kept for API parity.
local IM_PAYLOAD = {}
IM_PAYLOAD.__index = IM_PAYLOAD
function IM_PAYLOAD:Clear() ImGuiPayload_Clear(self) end
function IM_PAYLOAD:IsDataType(t) return ImGuiPayload_IsDataType(self, t) end
function IM_PAYLOAD:IsPreview() return self.Preview end
function IM_PAYLOAD:IsDelivery() return self.Delivery end
function ImGuiPayload()
return setmetatable({ Data = nil, DataSize = 0, SourceId = 0, SourceParentId = 0, DataFrameCount = -1, DataType = "", Preview = false, Delivery = false }, IM_PAYLOAD)
end
function ImGuiPayload_Clear(p) p.SourceId = 0; p.SourceParentId = 0; p.Data = nil; p.DataSize = 0; p.DataType = ""; p.DataFrameCount = -1; p.Preview = false; p.Delivery = false end
function ImGuiPayload_IsDataType(p, t) return p.DataFrameCount ~= -1 and p.DataType == t end
function ImGuiPayload_IsPreview(p) return p.Preview end
function ImGuiPayload_IsDelivery(p) return p.Delivery end
--- @class ImGuiTextBuffer # string builder: Buf is a list of string chunks
local ImGuiTextBufferMT = {}
ImGuiTextBufferMT.__index = ImGuiTextBufferMT
function ImGuiTextBuffer() return setmetatable({ Buf = {} }, ImGuiTextBufferMT) end
function ImGuiTextBufferMT:clear() self.Buf = {} end
function ImGuiTextBufferMT:empty() return #self.Buf == 0 end
function ImGuiTextBufferMT:append(str) self.Buf[#self.Buf + 1] = str end
function ImGuiTextBufferMT:appendf(fmt, ...) self.Buf[#self.Buf + 1] = string.format(fmt, ...) end
function ImGuiTextBufferMT:c_str() return table.concat(self.Buf) end
function ImGuiTextBufferMT:size() return #self:c_str() end
--- @class ImGuiSettingsHandler
--- Fns take (ctx, handler, ...) like upstream. ReadOpenFn(ctx, handler, name) returns entry; ReadLineFn(ctx, handler, entry, line);
--- WriteAllFn(ctx, handler, buf: ImGuiTextBuffer)
function ImGuiSettingsHandler()
return { TypeName = nil, TypeHash = 0, ClearAllFn = nil, ReadInitFn = nil, ReadOpenFn = nil, ReadLineFn = nil,
ApplyAllFn = nil, WriteAllFn = nil, CleanupFn = nil, UserData = nil }
end
return true -- [Roblox] ModuleScripts must return exactly one value
+223
View File
@@ -33,6 +33,44 @@ function ImGui._ByteArrayToString(buf, buf_begin, buf_end)
return table.concat(str)
end
--- ImPool<T>: pool of objects keyed by ID. Public indices are 0-based like upstream (Buf is stored 1-based).
--- `ctor` creates a new element. Removed slots are recycled via FreeIdx (-1 = none; free slots chain through `_NextFree`).
local IM_POOL = {}
IM_POOL.__index = IM_POOL
function ImPool(ctor) return setmetatable({ Buf = {}, Map = {}, FreeIdx = 0, AliveCount = 0, BufSize = 0, _Ctor = ctor }, IM_POOL) end
function IM_POOL:GetByKey(key) local i = self.Map[key]; return i and self.Buf[i + 1] or nil end
function IM_POOL:GetByIndex(n) return self.Buf[n + 1] end
function IM_POOL:GetIndex(p) return p._PoolIdx end
function IM_POOL:Contains(p) return p ~= nil and self.Buf[(p._PoolIdx or -1) + 1] == p end
function IM_POOL:GetMapSize() return self.BufSize end
function IM_POOL:Clear() self.Buf = {}; self.Map = {}; self.FreeIdx = 0; self.AliveCount = 0; self.BufSize = 0 end
function IM_POOL:Add()
local idx
if self._Free and #self._Free > 0 then idx = table.remove(self._Free) else idx = self.BufSize; self.BufSize = self.BufSize + 1 end
local p = self._Ctor and self._Ctor() or {}
p._PoolIdx = idx
self.Buf[idx + 1] = p
self.AliveCount = self.AliveCount + 1
return p
end
function IM_POOL:GetOrAddByKey(key)
local i = self.Map[key]
if i then return self.Buf[i + 1] end
local p = self:Add(); self.Map[key] = p._PoolIdx; return p
end
function IM_POOL:Remove(key, p)
if type(key) == "table" then p = key; key = nil end
if key == nil then for k, i in pairs(self.Map) do if i == p._PoolIdx then key = k break end end end
if key ~= nil then self.Map[key] = nil end
self.Buf[p._PoolIdx + 1] = false
self._Free = self._Free or {}
table.insert(self._Free, p._PoolIdx)
self.AliveCount = self.AliveCount - 1
end
function IM_POOL:Reserve(n) end
-- NOTE: removed slots hold `false`; iterate with `for i = 0, pool:GetMapSize() - 1 do local t = pool:GetByIndex(i) if t then ... end end`
--- @type ImGuiContext?
local GImGui
@@ -1162,6 +1200,15 @@ function ImGuiNextWindowData()
ScrollVal = ImVec2(),
WindowFlags = nil,
ChildFlags = nil,
WindowFlagsSet = 0,
WindowFlagsClear = 0,
ChildFlagsSet = 0,
ChildFlagsClear = 0,
ViewportId = 0,
DockId = 0,
DockCond = 0,
PosUndock = false,
WindowClass = nil,
CollapsedVal = nil,
SizeConstraintRect = nil,
SizeCallback = nil,
@@ -1293,6 +1340,7 @@ function ImGuiStyle()
PopupBorderSize = 1.0,
DockingSeparatorSize = 2.0,
DockingNodeHasCloseButton = true,
MouseCursorScale = 1.0,
ItemSpacing = ImVec2(8, 4),
@@ -1655,6 +1703,91 @@ end
--- @field DebugAllocInfo ImGuiDebugAllocInfo
--- @field TempBuffer ImVector<char>
----------------------------------------------------------------
-- [SECTION] Tab bar, Tab item support (public flags from imgui.h + private extensions)
----------------------------------------------------------------
--- @enum ImGuiTabBarFlags
ImGuiTabBarFlags = {
None = 0,
Reorderable = bitLShift(1, 0),
AutoSelectNewTabs = bitLShift(1, 1),
TabListPopupButton = bitLShift(1, 2),
NoCloseWithMiddleMouseButton = bitLShift(1, 3),
NoTabListScrollingButtons = bitLShift(1, 4),
NoTooltip = bitLShift(1, 5),
DrawSelectedOverline = bitLShift(1, 6),
FittingPolicyMixed = bitLShift(1, 7),
FittingPolicyShrink = bitLShift(1, 8),
FittingPolicyScroll = bitLShift(1, 9),
-- Private
DockNode = bitLShift(1, 20),
IsFocused = bitLShift(1, 21),
SaveSettings = bitLShift(1, 22),
}
ImGuiTabBarFlags.FittingPolicyMask_ = bitOr(ImGuiTabBarFlags.FittingPolicyMixed, ImGuiTabBarFlags.FittingPolicyShrink, ImGuiTabBarFlags.FittingPolicyScroll)
ImGuiTabBarFlags.FittingPolicyDefault_ = ImGuiTabBarFlags.FittingPolicyMixed
ImGuiTabBarFlags.FittingPolicyResizeDown = ImGuiTabBarFlags.FittingPolicyShrink
--- @enum ImGuiTabItemFlags
ImGuiTabItemFlags = {
None = 0,
UnsavedDocument = bitLShift(1, 0),
SetSelected = bitLShift(1, 1),
NoCloseWithMiddleMouseButton = bitLShift(1, 2),
NoPushId = bitLShift(1, 3),
NoTooltip = bitLShift(1, 4),
NoReorder = bitLShift(1, 5),
Leading = bitLShift(1, 6),
Trailing = bitLShift(1, 7),
NoAssumedClosure = bitLShift(1, 8),
-- Private
NoCloseButton = bitLShift(1, 20),
Button = bitLShift(1, 21),
Invisible = bitLShift(1, 22),
Unsorted = bitLShift(1, 23),
}
ImGuiTabItemFlags.SectionMask_ = bitOr(ImGuiTabItemFlags.Leading, ImGuiTabItemFlags.Trailing)
-- Storage for one active tab item. In this port the label is stored in `Name` (C++ uses NameOffset into TabBar.TabsNames)
--- @class ImGuiTabItem
--- @return ImGuiTabItem
function ImGuiTabItem()
return {
ID = 0, Flags = 0, Window = nil,
LastFrameVisible = -1, LastFrameSelected = -1,
Offset = 0.0, Width = 0.0, ContentWidth = 0.0, RequestedWidth = -1.0,
NameOffset = -1, Name = nil,
BeginOrder = -1, -- 0-based
IndexDuringLayout = -1, -- 0-based
WantClose = false,
}
end
-- Storage for a tab bar. Tabs: ImVector of ImGuiTabItem tables (references; "pointers" stay valid across reorders)
--- @class ImGuiTabBar
--- @return ImGuiTabBar
function ImGuiTabBar()
return {
Window = nil, Tabs = ImVector(), Flags = 0, ID = 0,
SelectedTabId = 0, NextSelectedTabId = 0, NextScrollToTabId = 0, VisibleTabId = 0,
CurrFrameVisible = -1, PrevFrameVisible = -1,
BarRect = ImRect(), BarRectPrevWidth = 0.0,
CurrTabsContentsHeight = 0.0, PrevTabsContentsHeight = 0.0,
WidthAllTabs = 0.0, WidthAllTabsIdeal = 0.0,
ScrollingAnim = 0.0, ScrollingTarget = 0.0, ScrollingTargetDistToVisibility = 0.0, ScrollingSpeed = 0.0,
ScrollingRectMinX = 0.0, ScrollingRectMaxX = 0.0, SeparatorMinX = 0.0, SeparatorMaxX = 0.0,
ReorderRequestTabId = 0, ReorderRequestOffset = 0, BeginCount = 0,
WantLayout = false, VisibleTabWasSubmitted = false, TabsAddedNew = false, ScrollButtonEnabled = false,
TabsActiveCount = 0,
LastTabItemIdx = -1, -- 0-based
ItemSpacingY = 0.0, FramePadding = ImVec2(), BackupCursorPos = ImVec2(),
}
end
--- @class ImGuiShrinkWidthItem
function ImGuiShrinkWidthItem() return { Index = 0, Width = 0.0, InitialWidth = 0.0 } end
--- @param shared_font_atlas? ImFontAtlas
--- @return ImGuiContext
--- @nodiscard
@@ -1881,6 +2014,15 @@ function ImGuiContext(shared_font_atlas) -- TODO: tidy up / complete this struct
ComboPreviewData = ImGuiComboPreviewData(),
CurrentTabBar = nil,
TabBars = ImPool(ImGuiTabBar), -- ImPool<ImGuiTabBar>
-- Docking
DockContext = ImGuiDockContext(),
DockNodeWindowMenuHandler = nil,
DebugHoveredDockNode = nil,
CurrentTabBarStack = ImVector(), -- holds ImGuiTabBar references directly (no ImGuiPtrOrIndex needed in Lua)
ShrinkWidthBuffer = ImVector(), -- ImGuiShrinkWidthItem
WindowResizeBorderExpectedRect = ImRect(),
WindowResizeRelativeMode = false,
@@ -1914,16 +2056,61 @@ function ImGuiContext(shared_font_atlas) -- TODO: tidy up / complete this struct
UserTextures = ImVector(),
-- Settings
SettingsLoaded = false,
SettingsDirtyTimer = 0.0,
SettingsIniData = "", -- In memory .ini settings (string)
SettingsHandlers = ImVector(), -- List of .ini settings handlers
SettingsWindows = ImVector(),
SettingsTables = ImVector(),
-- Hooks
Hooks = ImVector(),
HookIdNext = 0,
-- Drag and Drop
DragDropActive = false,
DragDropWithinSource = false,
DragDropWithinTarget = false,
DragDropSourceFlags = 0,
DragDropSourceFrameCount = -1,
DragDropMouseButton = -1,
DragDropPayload = ImGuiPayload(),
DragDropTargetRect = ImRect(),
DragDropTargetClipRect = ImRect(),
DragDropTargetId = 0,
DragDropTargetFullViewport = 0,
DragDropAcceptFlagsCurr = 0, DragDropAcceptFlagsPrev = 0,
DragDropAcceptIdCurrRectSurface = 0.0,
DragDropAcceptFrameCount = -1,
DragDropHoldJustPressedId = 0,
DragDropAcceptIdPrev = 0, DragDropAcceptIdCurr = 0,
-- Clipper
ClipperTempDataStacked = 0,
ClipperTempData = ImVector(),
-- Logging
LogEnabled = false,
LogLineFirstItem = false,
LogFlags = 0,
LogWindow = nil,
LogFile = nil,
LogBuffer = ImGuiTextBuffer(),
LogNextPrefix = nil, LogNextSuffix = nil,
LogLinePosY = FLT_MAX,
LogDepthRef = 0,
LogDepthToExpand = 2, LogDepthToExpandDefault = 2,
-- Error handling
ErrorCallback = nil, ErrorCallbackUserData = nil,
ErrorTooltipLockedPos = ImVec2(),
ErrorFirst = true,
ErrorCountCurrentFrame = 0,
StackSizesInNewFrame = nil,
ContextName = "",
DebugBreakInWindow = 0,
LocalizationTable = {},
DebugLogFlags = bitOr(ImGuiDebugLogFlags.EventError, ImGuiDebugLogFlags.OutputToTTY),
@@ -1942,6 +2129,16 @@ function ImGuiContext(shared_font_atlas) -- TODO: tidy up / complete this struct
WantCaptureKeyboardNextFrame = -1,
WantTextInputNextFrame = -1,
TempBuffer = ImVector(),
-- Tables (imgui_tables.lua)
CurrentTable = nil,
DebugBreakInTable = 0,
TablesTempDataStacked = 0,
TablesTempData = ImVector(),
Tables = ImPool(), -- ImPool<ImGuiTable>, 0-based indices (imgui_tables.lua)
TablesLastTimeActive = ImVector(), -- [table_idx + 1] = float
DrawChannelsTempMergeBuffer = ImVector(),
SettingsTables = ImVector(), -- ImChunkStream<ImGuiTableSettings>: plain vector, SettingsOffset = 1-based index
}
for i = 0, 59 do this.FramerateSecPerFrame[i] = 0 end
@@ -1989,6 +2186,12 @@ function ImGuiWindowSettings()
WantApply = false,
WantDelete = false,
Name = "",
ViewportId = 0,
ViewportPos = ImVec2(0, 0),
DockId = 0,
DockOrder = -1,
ClassId = 0,
LastUsedDate = 0,
}, IMGUI_WINDOW_SETTINGS)
end
@@ -2049,6 +2252,8 @@ end
--- @nodiscard
local function ImGuiWindowTempData()
return {
DockTabItemStatusFlags = 0,
DockTabItemRect = ImRect(),
CursorPos = ImVec2(),
CursorPosPrevLine = ImVec2(),
CursorStartPos = ImVec2(),
@@ -2082,6 +2287,8 @@ local function ImGuiWindowTempData()
TreeRecordsClippedNodesY2Mask = nil,
ChildWindows = ImVector(),
CurrentTableIdx = -1, -- 0-based index into g.Tables, -1 if none
CurrentColumns = nil, -- ImGuiOldColumns (legacy Columns API)
StateStorage = nil,
LayoutType = nil
@@ -2274,6 +2481,7 @@ function ImGuiWindow(ctx, name)
LastTimeActive = -1.0,
StateStorage = {},
ColumnsStorage = ImVector(), -- ImVector<ImGuiOldColumns>
WriteAccessed = false,
@@ -2297,6 +2505,21 @@ function ImGuiWindow(ctx, name)
this.ID = ImHashStr(name)
this.IDStack:push_back(this.ID)
this.MoveId = this:GetID("#MOVE")
this.TabId = this:GetID("#TAB")
-- Docking
this.ChildId = this.ChildId or 0
this.SetWindowDockAllowFlags = 0
this.ParentWindowForFocusRoute = nil
this.DockIsActive = false
this.DockNodeIsVisible = false
this.DockTabIsVisible = false
this.DockTabWantClose = false
this.DockOrder = -1
this.DockStyle = { Colors = {} }
for i = 0, 8 do this.DockStyle.Colors[i] = 0 end
this.DockNode = nil
this.DockNodeAsHost = nil
this.DockId = 0
return this
end
+1164
View File
File diff suppressed because it is too large Load Diff
+187
View File
@@ -301,6 +301,28 @@ function ImGui.BulletText(fmt, ...)
ImGui.RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0), text, 1, text_end, false)
end
-- va_list variants: identical to the varargs versions in Lua
ImGui.TextColoredV = ImGui.TextColored
ImGui.TextDisabledV = ImGui.TextDisabled
ImGui.TextWrappedV = ImGui.TextWrapped
ImGui.TextAlignedV = ImGui.TextAligned
ImGui.LabelTextV = ImGui.LabelText
ImGui.BulletTextV = ImGui.BulletText
-- Value(prefix, b) / Value(prefix, int) / Value(prefix, float, float_format)
-- Lua can't tell int from float: integers without float_format print as %d, otherwise %.3f
function ImGui.Value(prefix, v, float_format)
if type(v) == "boolean" then
ImGui.Text("%s: %s", prefix, v and "true" or "false")
elseif float_format then
ImGui.Text("%s: " .. float_format, prefix, v)
elseif math.floor(v) == v then
ImGui.Text("%s: %d", prefix, v)
else
ImGui.Text("%s: %.3f", prefix, v)
end
end
----------------------------------------------------------------
-- [SECTION] MAIN: BUTTONS, SCROLLBARS, ...
----------------------------------------------------------------
@@ -1198,6 +1220,7 @@ function ImGui.CheckboxFlags(label, flags, flags_value)
return pressed, flags
end
ImGui.CheckboxFlagsT = ImGui.CheckboxFlags
--- @param label string
--- @param active bool
@@ -2625,6 +2648,26 @@ function ImGui.InputFloat3(label, v, format, flags)
return ImGui.InputScalarN(label, ImGuiDataType.Float, v, 3, nil, nil, format, flags)
end
function ImGui.InputFloat2(label, v, format, flags)
return ImGui.InputScalarN(label, ImGuiDataType.Float, v, 2, nil, nil, format or "%.3f", flags)
end
function ImGui.InputFloat4(label, v, format, flags)
return ImGui.InputScalarN(label, ImGuiDataType.Float, v, 4, nil, nil, format or "%.3f", flags)
end
function ImGui.InputInt2(label, v, flags)
return ImGui.InputScalarN(label, ImGuiDataType.S32, v, 2, nil, nil, "%d", flags)
end
function ImGui.InputInt3(label, v, flags)
return ImGui.InputScalarN(label, ImGuiDataType.S32, v, 3, nil, nil, "%d", flags)
end
function ImGui.InputInt4(label, v, flags)
return ImGui.InputScalarN(label, ImGuiDataType.S32, v, 4, nil, nil, "%d", flags)
end
--- @param label string
--- @param v int
--- @param step? int
@@ -3041,6 +3084,112 @@ function ImGui.DragInt(label, v, v_speed, v_min, v_max, format, flags)
return ImGui.DragScalar(label, ImGuiDataType.S32, v, v_speed, v_min, v_max, format, flags)
end
-- `v` is mutated in place (1-based), returns value_changed
function ImGui.DragScalarN(label, data_type, v, components, v_speed, v_min, v_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 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 bit32.band(flags, ImGuiSliderFlags.ColorMarkers) ~= 0 then
ImGui.SetNextItemColorMarker(GDefaultRgbaColorMarkers[i])
end
local changed
v[i], changed = ImGui.DragScalar("", data_type, v[i], v_speed, 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
local function DragFloatN(n)
return function(label, v, v_speed, v_min, v_max, format, flags)
return ImGui.DragScalarN(label, ImGuiDataType.Float, v, n, v_speed or 1.0, v_min or 0.0, v_max or 0.0, format or "%.3f", flags or 0)
end
end
local function DragIntN(n)
return function(label, v, v_speed, v_min, v_max, format, flags)
return ImGui.DragScalarN(label, ImGuiDataType.S32, v, n, v_speed or 1.0, v_min or 0, v_max or 0, format or "%d", flags or 0)
end
end
ImGui.DragFloat2 = DragFloatN(2)
ImGui.DragFloat3 = DragFloatN(3)
ImGui.DragFloat4 = DragFloatN(4)
ImGui.DragInt2 = DragIntN(2)
ImGui.DragInt3 = DragIntN(3)
ImGui.DragInt4 = DragIntN(4)
-- NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this.
--- @return float v_current_min
--- @return float v_current_max
--- @return bool value_changed
local function DragRange2(data_type, label, v_current_min, v_current_max, v_speed, v_min, v_max, format, format_max, flags)
local window = ImGui.GetCurrentWindow()
if window.SkipItems then
return v_current_min, v_current_max, false
end
local g = GImGui
local lo = (data_type == ImGuiDataType.Float) and -FLT_MAX or INT_MIN
local hi = (data_type == ImGuiDataType.Float) and FLT_MAX or INT_MAX
ImGui.PushID(label)
ImGui.BeginGroup()
ImGui.PushMultiItemsWidths(2, ImGui.CalcItemWidth())
local min_min = (v_min >= v_max) and lo or v_min
local min_max = (v_min >= v_max) and v_current_max or ImMin(v_max, v_current_max)
local min_flags = bit32.bor(flags, (min_min == min_max) and ImGuiSliderFlags.ReadOnly or 0)
local value_changed, changed
v_current_min, value_changed = ImGui.DragScalar("##min", data_type, v_current_min, v_speed, min_min, min_max, format, min_flags)
ImGui.PopItemWidth()
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
local max_min = (v_min >= v_max) and v_current_min or ImMax(v_min, v_current_min)
local max_max = (v_min >= v_max) and hi or v_max
local max_flags = bit32.bor(flags, (max_min == max_max) and ImGuiSliderFlags.ReadOnly or 0)
v_current_max, changed = ImGui.DragScalar("##max", data_type, v_current_max, v_speed, max_min, max_max, format_max or format, max_flags)
value_changed = value_changed or changed
ImGui.PopItemWidth()
ImGui.SameLine(0, g.Style.ItemInnerSpacing.x)
ImGui.TextEx(label, ImGui.FindRenderedTextEnd(label))
ImGui.EndGroup()
ImGui.PopID()
return v_current_min, v_current_max, value_changed
end
function ImGui.DragFloatRange2(label, v_current_min, v_current_max, v_speed, v_min, v_max, format, format_max, flags)
return DragRange2(ImGuiDataType.Float, label, v_current_min, v_current_max, v_speed or 1.0, v_min or 0.0, v_max or 0.0, format or "%.3f", format_max, flags or 0)
end
function ImGui.DragIntRange2(label, v_current_min, v_current_max, v_speed, v_min, v_max, format, format_max, flags)
return DragRange2(ImGuiDataType.S32, label, v_current_min, v_current_max, v_speed or 1.0, v_min or 0, v_max or 0, format or "%d", format_max, flags or 0)
end
----------------------------------------------------------------
-- [SECTION] SLIDERXXX
----------------------------------------------------------------
@@ -3651,6 +3800,18 @@ function ImGui.SliderInt(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalar(label, ImGuiDataType.S32, v, v_min, v_max, format, flags)
end
function ImGui.SliderInt2(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, ImGuiDataType.S32, v, 2, v_min, v_max, format or "%d", flags)
end
function ImGui.SliderInt3(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, ImGuiDataType.S32, v, 3, v_min, v_max, format or "%d", flags)
end
function ImGui.SliderInt4(label, v, v_min, v_max, format, flags)
return ImGui.SliderScalarN(label, ImGuiDataType.S32, v, 4, v_min, v_max, format or "%d", flags)
end
--- @param label string
--- @param size ImVec2
--- @param data_type ImGuiDataType
@@ -3764,6 +3925,11 @@ function ImGui.InputText(label, buf, buf_size, flags, callback, user_data)
return ImGui.InputTextEx(label, nil, buf, buf_size, ImVec2(0, 0), flags, callback, user_data)
end
--- @param size? ImVec2
function ImGui.InputTextMultiline(label, buf, buf_size, size, flags, callback, user_data)
return ImGui.InputTextEx(label, nil, buf, buf_size, size or ImVec2(0, 0), bit32.bor(flags or 0, ImGuiInputTextFlags.Multiline), callback, user_data)
end
ImStb = {}
--- @module "imstb_textedit"
@@ -5811,6 +5977,27 @@ function ImGui.ColorEdit3(label, col, flags)
return ImGui.ColorEdit4(label, col, bit32.bor(flags, ImGuiColorEditFlags.NoAlpha))
end
-- `col` mutated in place, returns value_changed
function ImGui.ColorPicker3(label, col, flags)
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
end
col[1] = col4[1]; col[2] = col4[2]; col[3] = col4[3]
return true
end
function ImGui.SetColorEditOptions(flags)
local g = GImGui
local def = ImGuiColorEditFlags.DefaultOptions_
for _, mask in ipairs({ ImGuiColorEditFlags.DisplayMask_, ImGuiColorEditFlags.DataTypeMask_, ImGuiColorEditFlags.PickerMask_, ImGuiColorEditFlags.InputMask_ }) do
if bit32.band(flags, mask) == 0 then
flags = bit32.bor(flags, bit32.band(def, mask))
end
end
g.IO.ConfigColorEditFlags = flags
end
-- Helper for ColorPicker4()
--- @param draw_list ImDrawList
--- @param pos ImVec2