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
+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