Port debug tools (item picker, flash style color, debug locate, DebugDraw*, keyboard preview, debug break, DrawCmd mesh view), context hooks, SetContextName, AddInputCharactersUTF8, sanity checks, window resize ids, Register/UnregisterUserTexture, SetPixelDensity, TestShortcutRouting, DemoMarker; InputTextState selection API (fixes undo crash, callbacks get 0-based positions), ShortcutsForCancel (Esc/right-click reverts drags/sliders), ImParseFormatSanitizeForPrinting, ImDrawList CloneOutput/PrimQuadUV/AddImageQuad, ImDrawData DeIndexAllBuffers/ScaleClipRects, internal helpers (ImBitVector, ImGuiPackedDate, inline ImGui getters...); t_debugtools, t_helpers

This commit is contained in:
4DBug
2026-10-15 08:52:30 -05:00
parent 9be05aeb5c
commit e3840a938a
16 changed files with 7058 additions and 2073 deletions
+1 -1
View File
@@ -1 +1 @@
[[46, "imgui.lua"], [21466, "imgui_demo.lua"], [33318, "imgui_draw.lua"], [41521, "imgui_h.lua"], [44455, "imgui_impl_roblox.lua"], [46103, "imgui_impl_roblox_fonts.lua"], [52506, "imgui_internal.lua"], [56228, "imgui_tables.lua"], [61287, "imgui_widgets.lua"], [73263, "imstb_rectpack.lua"], [73927, "imstb_textedit.lua"], [75469, "imstb_truetype.lua"], [78257, "imstd_minstdio.lua"]] [[46, "imgui.lua"], [22580, "imgui_demo.lua"], [34431, "imgui_draw.lua"], [42736, "imgui_h.lua"], [45681, "imgui_impl_roblox.lua"], [47329, "imgui_impl_roblox_fonts.lua"], [53732, "imgui_internal.lua"], [57611, "imgui_tables.lua"], [62711, "imgui_widgets.lua"], [74825, "imstb_rectpack.lua"], [75489, "imstb_textedit.lua"], [77031, "imstb_truetype.lua"], [79819, "imstd_minstdio.lua"]]
+1765 -173
View File
File diff suppressed because it is too large Load Diff
+49
View File
@@ -6,6 +6,7 @@ export type ImVec4 = { x: number, y: number, z: number, w: number, [any]: any }
export type ImGuiAPI = { export type ImGuiAPI = {
AcceptDragDropPayload: (type: string, flags: number?) -> any, AcceptDragDropPayload: (type: string, flags: number?) -> any,
ActivateItemByID: (...any) -> ...any, ActivateItemByID: (...any) -> ...any,
AddContextHook: (...any) -> ...any,
AddDrawListToDrawDataEx: (...any) -> ...any, AddDrawListToDrawDataEx: (...any) -> ...any,
AddSettingsHandler: (...any) -> ...any, AddSettingsHandler: (...any) -> ...any,
AddUpdateViewport: (...any) -> ...any, AddUpdateViewport: (...any) -> ...any,
@@ -72,6 +73,7 @@ export type ImGuiAPI = {
CalcTypematicRepeatAmount: (...any) -> ...any, CalcTypematicRepeatAmount: (...any) -> ...any,
CalcWindowNextAutoFitSize: (...any) -> ...any, CalcWindowNextAutoFitSize: (...any) -> ...any,
CalcWrapWidthForPos: (...any) -> ...any, CalcWrapWidthForPos: (...any) -> ...any,
CallContextHooks: (...any) -> ...any,
Checkbox: (label: string, v: boolean?) -> (boolean, boolean?), Checkbox: (label: string, v: boolean?) -> (boolean, boolean?),
CheckboxFlags: (...any) -> ...any, CheckboxFlags: (...any) -> ...any,
CleanupIniSettings: (...any) -> ...any, CleanupIniSettings: (...any) -> ...any,
@@ -111,15 +113,29 @@ export type ImGuiAPI = {
DataTypeGetInfo: (...any) -> ...any, DataTypeGetInfo: (...any) -> ...any,
DataTypeIsZero: (...any) -> ...any, DataTypeIsZero: (...any) -> ...any,
DebugAllocHook: (...any) -> ...any, DebugAllocHook: (...any) -> ...any,
DebugBreakButton: (...any) -> ...any,
DebugBreakButtonTooltip: (...any) -> ...any,
DebugBreakClearData: (...any) -> ...any,
DebugDrawCursorPos: (...any) -> ...any,
DebugDrawItemRect: (...any) -> ...any,
DebugDrawLineExtents: (...any) -> ...any,
DebugFlashStyleColor: (idx: number) -> (),
DebugHookIdInfo: (...any) -> ...any, DebugHookIdInfo: (...any) -> ...any,
DebugLocateItem: (...any) -> ...any,
DebugLocateItemOnHover: (...any) -> ...any,
DebugLocateItemResolveWithLastItem: (...any) -> ...any,
DebugLog: (fmt: string, ...any) -> (), DebugLog: (fmt: string, ...any) -> (),
DebugLogV: (fmt: string, ...any) -> (),
DebugNodeColumns: (...any) -> ...any, DebugNodeColumns: (...any) -> ...any,
DebugNodeDockNode: (...any) -> ...any, DebugNodeDockNode: (...any) -> ...any,
DebugNodeDrawCmdShowMeshAndBoundingBox: (...any) -> ...any,
DebugNodeDrawList: (...any) -> ...any, DebugNodeDrawList: (...any) -> ...any,
DebugNodeFont: (...any) -> ...any, DebugNodeFont: (...any) -> ...any,
DebugNodeFontGlyph: (...any) -> ...any, DebugNodeFontGlyph: (...any) -> ...any,
DebugNodeFontGlyphsForSrcMask: (...any) -> ...any, DebugNodeFontGlyphsForSrcMask: (...any) -> ...any,
DebugNodeInputTextState: (...any) -> ...any,
DebugNodeMultiSelectState: (...any) -> ...any, DebugNodeMultiSelectState: (...any) -> ...any,
DebugNodePlatformMonitor: (...any) -> ...any,
DebugNodeStorage: (...any) -> ...any, DebugNodeStorage: (...any) -> ...any,
DebugNodeTabBar: (...any) -> ...any, DebugNodeTabBar: (...any) -> ...any,
DebugNodeTable: (...any) -> ...any, DebugNodeTable: (...any) -> ...any,
@@ -131,8 +147,13 @@ export type ImGuiAPI = {
DebugNodeWindowSettings: (...any) -> ...any, DebugNodeWindowSettings: (...any) -> ...any,
DebugNodeWindowsList: (...any) -> ...any, DebugNodeWindowsList: (...any) -> ...any,
DebugNodeWindowsListByBeginStackParent: (...any) -> ...any, DebugNodeWindowsListByBeginStackParent: (...any) -> ...any,
DebugRenderKeyboardPreview: (...any) -> ...any,
DebugRenderViewportThumbnail: (...any) -> ...any, DebugRenderViewportThumbnail: (...any) -> ...any,
DebugStartItemPicker: () -> (),
DebugTextEncoding: (str: string) -> (), DebugTextEncoding: (str: string) -> (),
DebugTextUnformattedWithLocateItem: (...any) -> ...any,
DebugTextureIDToU64: (...any) -> ...any,
DemoMarker: (...any) -> ...any,
DestroyContext: (ctx: any) -> ...any, DestroyContext: (ctx: any) -> ...any,
DestroyPlatformWindow: (...any) -> ...any, DestroyPlatformWindow: (...any) -> ...any,
DestroyPlatformWindows: () -> (), DestroyPlatformWindows: () -> (),
@@ -247,6 +268,8 @@ export type ImGuiAPI = {
EndTable: () -> (), EndTable: () -> (),
EndTooltip: () -> (), EndTooltip: () -> (),
ErrorCheckEndFrameFinalizeErrorTooltip: (...any) -> ...any, ErrorCheckEndFrameFinalizeErrorTooltip: (...any) -> ...any,
ErrorCheckEndFrameSanityChecks: (...any) -> ...any,
ErrorCheckNewFrameSanityChecks: (...any) -> ...any,
ErrorCheckUsingSetCursorPosToExtendParentBoundaries: (...any) -> ...any, ErrorCheckUsingSetCursorPosToExtendParentBoundaries: (...any) -> ...any,
ErrorLog: (...any) -> ...any, ErrorLog: (...any) -> ...any,
ErrorRecoveryStoreState: (...any) -> ...any, ErrorRecoveryStoreState: (...any) -> ...any,
@@ -265,6 +288,7 @@ export type ImGuiAPI = {
FindRenderedTextEnd: (...any) -> ...any, FindRenderedTextEnd: (...any) -> ...any,
FindSettingsHandler: (...any) -> ...any, FindSettingsHandler: (...any) -> ...any,
FindViewportByID: (viewport_id: number) -> any, FindViewportByID: (viewport_id: number) -> any,
FindViewportByPlatformHandle: (platform_handle: any) -> ...any,
FindWindowByID: (...any) -> ...any, FindWindowByID: (...any) -> ...any,
FindWindowByName: (...any) -> ...any, FindWindowByName: (...any) -> ...any,
FindWindowDisplayIndex: (...any) -> ...any, FindWindowDisplayIndex: (...any) -> ...any,
@@ -279,6 +303,7 @@ export type ImGuiAPI = {
GcAwakeTransientWindowBuffers: (...any) -> ...any, GcAwakeTransientWindowBuffers: (...any) -> ...any,
GcCompactTransientMiscBuffers: (...any) -> ...any, GcCompactTransientMiscBuffers: (...any) -> ...any,
GcCompactTransientWindowBuffers: (...any) -> ...any, GcCompactTransientWindowBuffers: (...any) -> ...any,
GetActiveID: (...any) -> ...any,
GetBackgroundDrawList: (viewport: any) -> ...any, GetBackgroundDrawList: (viewport: any) -> ...any,
GetBoxSelectState: (...any) -> ...any, GetBoxSelectState: (...any) -> ...any,
GetClipboardText: () -> any, GetClipboardText: () -> any,
@@ -293,6 +318,9 @@ export type ImGuiAPI = {
GetContentRegionAvail: () -> ImVec2, GetContentRegionAvail: () -> ImVec2,
GetContentRegionMax: (...any) -> ...any, GetContentRegionMax: (...any) -> ...any,
GetCurrentContext: () -> any, GetCurrentContext: () -> any,
GetCurrentFocusScope: (...any) -> ...any,
GetCurrentTabBar: (...any) -> ...any,
GetCurrentTable: (...any) -> ...any,
GetCurrentWindow: (...any) -> ...any, GetCurrentWindow: (...any) -> ...any,
GetCurrentWindowRead: (...any) -> ...any, GetCurrentWindowRead: (...any) -> ...any,
GetCursorPos: () -> ImVec2, GetCursorPos: () -> ImVec2,
@@ -304,7 +332,9 @@ export type ImGuiAPI = {
GetDragDropPayload: () -> any, GetDragDropPayload: () -> any,
GetDrawData: () -> any, GetDrawData: () -> any,
GetDrawListSharedData: () -> any, GetDrawListSharedData: () -> any,
GetFocusID: (...any) -> ...any,
GetFont: () -> any, GetFont: () -> any,
GetFontBaked: () -> any,
GetFontSize: () -> number, GetFontSize: () -> number,
GetFontTexUvWhitePixel: () -> ImVec2, GetFontTexUvWhitePixel: () -> ImVec2,
GetForegroundDrawList: (viewport: any) -> ...any, GetForegroundDrawList: (viewport: any) -> ...any,
@@ -322,6 +352,7 @@ export type ImGuiAPI = {
GetItemRectMax: () -> ImVec2, GetItemRectMax: () -> ImVec2,
GetItemRectMin: () -> ImVec2, GetItemRectMin: () -> ImVec2,
GetItemRectSize: () -> ImVec2, GetItemRectSize: () -> ImVec2,
GetItemStatusFlags: (...any) -> ...any,
GetKeyChordName: (...any) -> ...any, GetKeyChordName: (...any) -> ...any,
GetKeyData: (...any) -> ...any, GetKeyData: (...any) -> ...any,
GetKeyMagnitude2d: (...any) -> ...any, GetKeyMagnitude2d: (...any) -> ...any,
@@ -340,6 +371,7 @@ export type ImGuiAPI = {
GetMousePosOnOpeningCurrentPopup: () -> ImVec2, GetMousePosOnOpeningCurrentPopup: () -> ImVec2,
GetMultiSelectState: (...any) -> ...any, GetMultiSelectState: (...any) -> ...any,
GetNavTweakPressedAmount: (...any) -> ...any, GetNavTweakPressedAmount: (...any) -> ...any,
GetPixelDensity: (...any) -> ...any,
GetPlatformIO: () -> any, GetPlatformIO: () -> any,
GetPopupAllowedExtentRect: (...any) -> ...any, GetPopupAllowedExtentRect: (...any) -> ...any,
GetRoundedFontSize: (...any) -> ...any, GetRoundedFontSize: (...any) -> ...any,
@@ -369,10 +401,13 @@ export type ImGuiAPI = {
GetWindowContentRegionMax: (...any) -> ...any, GetWindowContentRegionMax: (...any) -> ...any,
GetWindowContentRegionMin: (...any) -> ...any, GetWindowContentRegionMin: (...any) -> ...any,
GetWindowDockID: () -> number, GetWindowDockID: () -> number,
GetWindowDockNode: (...any) -> ...any,
GetWindowDpiScale: () -> number, GetWindowDpiScale: () -> number,
GetWindowDrawList: () -> any, GetWindowDrawList: () -> any,
GetWindowHeight: () -> number, GetWindowHeight: () -> number,
GetWindowPos: () -> ImVec2, GetWindowPos: () -> ImVec2,
GetWindowResizeBorderID: (...any) -> ...any,
GetWindowResizeCornerID: (...any) -> ...any,
GetWindowScrollbarID: (...any) -> ...any, GetWindowScrollbarID: (...any) -> ...any,
GetWindowScrollbarRect: (...any) -> ...any, GetWindowScrollbarRect: (...any) -> ...any,
GetWindowSize: () -> ImVec2, GetWindowSize: () -> ImVec2,
@@ -414,6 +449,7 @@ export type ImGuiAPI = {
IsInNavFocusRoute: (...any) -> ...any, IsInNavFocusRoute: (...any) -> ...any,
IsItemActivated: () -> boolean, IsItemActivated: () -> boolean,
IsItemActive: () -> boolean, IsItemActive: () -> boolean,
IsItemActiveAsInputText: (...any) -> ...any,
IsItemClicked: (mouse_button: number?) -> boolean, IsItemClicked: (mouse_button: number?) -> boolean,
IsItemDeactivated: () -> boolean, IsItemDeactivated: () -> boolean,
IsItemDeactivatedAfterEdit: () -> boolean, IsItemDeactivatedAfterEdit: () -> boolean,
@@ -430,6 +466,7 @@ export type ImGuiAPI = {
IsKeyReleased: (...any) -> ...any, IsKeyReleased: (...any) -> ...any,
IsKeyboardKey: (...any) -> ...any, IsKeyboardKey: (...any) -> ...any,
IsLRModKey: (...any) -> ...any, IsLRModKey: (...any) -> ...any,
IsLegacyKey: (...any) -> ...any,
IsMouseClicked: (button: number, is_repeat: boolean?) -> boolean, IsMouseClicked: (button: number, is_repeat: boolean?) -> boolean,
IsMouseClickedEx: (...any) -> ...any, IsMouseClickedEx: (...any) -> ...any,
IsMouseDoubleClicked: (...any) -> ...any, IsMouseDoubleClicked: (...any) -> ...any,
@@ -569,6 +606,8 @@ export type ImGuiAPI = {
RadioButton: (...any) -> ...any, RadioButton: (...any) -> ...any,
RadioButtonEx: (...any) -> ...any, RadioButtonEx: (...any) -> ...any,
RegisterFontAtlas: (...any) -> ...any, RegisterFontAtlas: (...any) -> ...any,
RegisterUserTexture: (...any) -> ...any,
RemoveContextHook: (...any) -> ...any,
RemoveSettingsHandler: (...any) -> ...any, RemoveSettingsHandler: (...any) -> ...any,
Render: () -> (), Render: () -> (),
RenderArrow: (...any) -> ...any, RenderArrow: (...any) -> ...any,
@@ -604,6 +643,7 @@ export type ImGuiAPI = {
ScaleRatioFromValueT: (...any) -> ...any, ScaleRatioFromValueT: (...any) -> ...any,
ScaleValueFromRatioT: (...any) -> ...any, ScaleValueFromRatioT: (...any) -> ...any,
ScaleWindowsInViewport: (...any) -> ...any, ScaleWindowsInViewport: (...any) -> ...any,
ScrollToBringRectIntoView: (...any) -> ...any,
ScrollToItem: (...any) -> ...any, ScrollToItem: (...any) -> ...any,
ScrollToRect: (...any) -> ...any, ScrollToRect: (...any) -> ...any,
ScrollToRectEx: (...any) -> ...any, ScrollToRectEx: (...any) -> ...any,
@@ -621,6 +661,7 @@ export type ImGuiAPI = {
SetColorEditOptions: (...any) -> ...any, SetColorEditOptions: (...any) -> ...any,
SetColumnOffset: (column_index: number, offset: number) -> (), SetColumnOffset: (column_index: number, offset: number) -> (),
SetColumnWidth: (column_index: number, width: number) -> (), SetColumnWidth: (column_index: number, width: number) -> (),
SetContextName: (...any) -> ...any,
SetCurrentContext: (ctx: any) -> ...any, SetCurrentContext: (ctx: any) -> ...any,
SetCurrentFont: (...any) -> ...any, SetCurrentFont: (...any) -> ...any,
SetCurrentViewport: (...any) -> ...any, SetCurrentViewport: (...any) -> ...any,
@@ -634,6 +675,7 @@ export type ImGuiAPI = {
SetItemDefaultFocus: () -> (), SetItemDefaultFocus: () -> (),
SetItemKeyOwner: (...any) -> ...any, SetItemKeyOwner: (...any) -> ...any,
SetItemTooltip: (fmt: string, ...any) -> (), SetItemTooltip: (fmt: string, ...any) -> (),
SetItemTooltipV: (fmt: string, ...any) -> (),
SetKeyOwner: (...any) -> ...any, SetKeyOwner: (...any) -> ...any,
SetKeyOwnersForKeyChord: (...any) -> ...any, SetKeyOwnersForKeyChord: (...any) -> ...any,
SetKeyboardFocusHere: (offset: number?) -> (), SetKeyboardFocusHere: (offset: number?) -> (),
@@ -671,6 +713,7 @@ export type ImGuiAPI = {
SetNextWindowSize: (size: ImVec2, cond: number?) -> (), SetNextWindowSize: (size: ImVec2, cond: number?) -> (),
SetNextWindowSizeConstraints: (size_min: ImVec2, size_max: ImVec2, custom_callback: any, custom_callback_user_data: any) -> ...any, SetNextWindowSizeConstraints: (size_min: ImVec2, size_max: ImVec2, custom_callback: any, custom_callback_user_data: any) -> ...any,
SetNextWindowViewport: (id: number) -> (), SetNextWindowViewport: (id: number) -> (),
SetPixelDensity: (...any) -> ...any,
SetScrollFromPosX: (...any) -> ...any, SetScrollFromPosX: (...any) -> ...any,
SetScrollFromPosY: (...any) -> ...any, SetScrollFromPosY: (...any) -> ...any,
SetScrollHereX: (center_x_ratio: number?) -> (), SetScrollHereX: (center_x_ratio: number?) -> (),
@@ -681,6 +724,7 @@ export type ImGuiAPI = {
SetStateStorage: (tree: any) -> ...any, SetStateStorage: (tree: any) -> ...any,
SetTabItemClosed: (label: string) -> (), SetTabItemClosed: (label: string) -> (),
SetTooltip: (fmt: string, ...any) -> (), SetTooltip: (fmt: string, ...any) -> (),
SetTooltipV: (fmt: string, ...any) -> (),
SetWindowClipRectBeforeSetChannel: (...any) -> ...any, SetWindowClipRectBeforeSetChannel: (...any) -> ...any,
SetWindowCollapsed: (...any) -> ...any, SetWindowCollapsed: (...any) -> ...any,
SetWindowDock: (...any) -> ...any, SetWindowDock: (...any) -> ...any,
@@ -688,6 +732,7 @@ export type ImGuiAPI = {
SetWindowFontScale: (...any) -> ...any, SetWindowFontScale: (...any) -> ...any,
SetWindowHiddenAndSkipItemsForCurrentFrame: (...any) -> ...any, SetWindowHiddenAndSkipItemsForCurrentFrame: (...any) -> ...any,
SetWindowHitTestHole: (...any) -> ...any, SetWindowHitTestHole: (...any) -> ...any,
SetWindowParentWindowForFocusRoute: (...any) -> ...any,
SetWindowPos: (...any) -> ...any, SetWindowPos: (...any) -> ...any,
SetWindowSize: (...any) -> ...any, SetWindowSize: (...any) -> ...any,
SetWindowViewport: (...any) -> ...any, SetWindowViewport: (...any) -> ...any,
@@ -836,6 +881,7 @@ export type ImGuiAPI = {
TempInputScalar: (...any) -> ...any, TempInputScalar: (...any) -> ...any,
TempInputText: (...any) -> ...any, TempInputText: (...any) -> ...any,
TestKeyOwner: (...any) -> ...any, TestKeyOwner: (...any) -> ...any,
TestShortcutRouting: (...any) -> ...any,
Text: (fmt: string, ...any) -> (), Text: (fmt: string, ...any) -> (),
TextAligned: (...any) -> ...any, TextAligned: (...any) -> ...any,
TextBufferToString: (buf: {number}) -> string, TextBufferToString: (buf: {number}) -> string,
@@ -867,9 +913,12 @@ export type ImGuiAPI = {
TypingSelectFindNextSingleCharMatch: (...any) -> ...any, TypingSelectFindNextSingleCharMatch: (...any) -> ...any,
Unindent: (indent_w: number?) -> (), Unindent: (indent_w: number?) -> (),
UnregisterFontAtlas: (...any) -> ...any, UnregisterFontAtlas: (...any) -> ...any,
UnregisterUserTexture: (...any) -> ...any,
UpdateAliasKey: (...any) -> ...any, UpdateAliasKey: (...any) -> ...any,
UpdateCurrentFontSize: (...any) -> ...any, UpdateCurrentFontSize: (...any) -> ...any,
UpdateDebugToolFlashStyleColor: (...any) -> ...any,
UpdateDebugToolItemPathQuery: (...any) -> ...any, UpdateDebugToolItemPathQuery: (...any) -> ...any,
UpdateDebugToolItemPicker: (...any) -> ...any,
UpdateFontsEndFrame: (...any) -> ...any, UpdateFontsEndFrame: (...any) -> ...any,
UpdateFontsNewFrame: (...any) -> ...any, UpdateFontsNewFrame: (...any) -> ...any,
UpdateHoveredWindowAndCaptureFlags: (...any) -> ...any, UpdateHoveredWindowAndCaptureFlags: (...any) -> ...any,
+1 -1
View File
@@ -1 +1 @@
[[183, "imgui.lua"], [21603, "imgui_demo.lua"], [33455, "imgui_draw.lua"], [41658, "imgui_h.lua"], [44592, "imgui_impl_love.lua"], [45132, "imgui_internal.lua"], [48854, "imgui_tables.lua"], [53913, "imgui_widgets.lua"], [65889, "imstb_rectpack.lua"], [66553, "imstb_textedit.lua"], [68095, "imstb_truetype.lua"], [70883, "imstd_minstdio.lua"]] [[183, "imgui.lua"], [22717, "imgui_demo.lua"], [34568, "imgui_draw.lua"], [42873, "imgui_h.lua"], [45818, "imgui_impl_love.lua"], [46358, "imgui_internal.lua"], [50237, "imgui_tables.lua"], [55337, "imgui_widgets.lua"], [67451, "imstb_rectpack.lua"], [68115, "imstb_textedit.lua"], [69657, "imstb_truetype.lua"], [72445, "imstd_minstdio.lua"]]
+1765 -173
View File
File diff suppressed because it is too large Load Diff
+1520 -1553
View File
File diff suppressed because it is too large Load Diff
+1194 -80
View File
File diff suppressed because it is too large Load Diff
+3 -4
View File
@@ -214,7 +214,7 @@ function ShowDockingDisabledMessage()
end end
-- Helper to wire demo markers located in code to an interactive browser (e.g. https://pthom.github.io/imgui_explorer) -- Helper to wire demo markers located in code to an interactive browser (e.g. https://pthom.github.io/imgui_explorer)
function IMGUI_DEMO_MARKER(section) end -- [Port] no ImGui.DemoMarker() hook in this port: no-op function IMGUI_DEMO_MARKER(section) ImGui.DemoMarker("imgui_demo.lua", 0, section) end -- [Port] no __LINE__ in Lua: line is 0
-- Sneakily forward declare functions which aren't worth putting in public API yet -- Sneakily forward declare functions which aren't worth putting in public API yet
-- [Port] ImGui.ShowFontAtlas(atlas) and ImGui.TreeNodeSetOpen(storage_id, is_open) are already public here. -- [Port] ImGui.ShowFontAtlas(atlas) and ImGui.TreeNodeSetOpen(storage_id, is_open) are already public here.
@@ -698,8 +698,7 @@ function DemoWindowMenuBar(demo_data)
_, demo_data.ShowDebugLog = ImGui.MenuItem("Debug Log", nil, demo_data.ShowDebugLog, has_debug_tools) _, demo_data.ShowDebugLog = ImGui.MenuItem("Debug Log", nil, demo_data.ShowDebugLog, has_debug_tools)
_, demo_data.ShowIDStackTool = ImGui.MenuItem("ID Stack Tool", nil, demo_data.ShowIDStackTool, has_debug_tools) _, demo_data.ShowIDStackTool = ImGui.MenuItem("ID Stack Tool", nil, demo_data.ShowIDStackTool, has_debug_tools)
local is_debugger_present = io.ConfigDebugIsDebuggerPresent local is_debugger_present = io.ConfigDebugIsDebuggerPresent
local has_item_picker = ImGui.DebugStartItemPicker ~= nil -- [Port] ImGui.DebugStartItemPicker() is not ported yet: menu item disabled until it is if ImGui.MenuItem("Item Picker", nil, false, has_debug_tools) then -- and is_debugger_present
if ImGui.MenuItem("Item Picker", nil, false, has_debug_tools and has_item_picker) then -- and is_debugger_present
ImGui.DebugStartItemPicker() ImGui.DebugStartItemPicker()
end end
if not is_debugger_present then if not is_debugger_present then
@@ -8904,7 +8903,7 @@ function ImGui.ShowStyleEditor(ref)
end end
ImGui.PushID(i) ImGui.PushID(i)
if ImGui.Button("?") then if ImGui.Button("?") then
if ImGui.DebugFlashStyleColor then ImGui.DebugFlashStyleColor(i) end -- [Port] DebugFlashStyleColor() is not in the library yet ImGui.DebugFlashStyleColor(i)
end end
ImGui.SetItemTooltip("Flash given color to identify places where it is used.") ImGui.SetItemTooltip("Flash given color to identify places where it is used.")
ImGui.SameLine() ImGui.SameLine()
+102
View File
@@ -4155,6 +4155,42 @@ function MT.ImDrawData:AddDrawList(draw_list)
ImGui.AddDrawListToDrawDataEx(self, self.CmdLists, draw_list) ImGui.AddDrawListToDrawDataEx(self, self.CmdLists, draw_list)
end end
-- For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
-- [Luau] IdxBuffer values are 1-based vertex indices
function MT.ImDrawData:DeIndexAllBuffers()
local new_vtx_buffer = ImDrawVtxBuffer()
self.TotalVtxCount = 0; self.TotalIdxCount = 0
for i = 1, self.CmdLists.Size do
repeat
local draw_list = self.CmdLists.Data[i]
if draw_list.IdxBuffer.Size == 0 then
break -- continue
end
new_vtx_buffer:resize(draw_list.IdxBuffer.Size)
local src, dst, idx = draw_list.VtxBuffer.Buf, new_vtx_buffer.Buf, draw_list.IdxBuffer.Data
for j = 1, draw_list.IdxBuffer.Size do
buffer.copy(dst, (j - 1) * VTX_STRIDE, src, (idx[j] - 1) * VTX_STRIDE, VTX_STRIDE)
end
draw_list.VtxBuffer, new_vtx_buffer = new_vtx_buffer, draw_list.VtxBuffer -- swap()
draw_list.IdxBuffer:resize(0)
self.TotalVtxCount = self.TotalVtxCount + draw_list.VtxBuffer.Size
until true
end
end
-- Helper to scale the ClipRect field of each ImDrawCmd.
-- Use if your final output buffer is at a different scale than draw_data->DisplaySize,
-- or if there is a difference between your window resolution and framebuffer resolution.
function MT.ImDrawData:ScaleClipRects(fb_scale)
for i = 1, self.CmdLists.Size do
local draw_list = self.CmdLists.Data[i]
for j = 1, draw_list.CmdBuffer.Size do
local cmd = draw_list.CmdBuffer.Data[j]
cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y)
end
end
end
local IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN = 4 local IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN = 4
local IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX = 512 local IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX = 512
@@ -4262,6 +4298,29 @@ function MT.ImDrawList:_ClearFreeMemory()
self._Splitter:ClearFreeMemory() self._Splitter:ClearFreeMemory()
end end
-- Note: For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club
-- [Luau] C++ copies the vectors by value: copy the ImDrawCmd structs and the vertex buffer bytes
--- @return ImDrawList
function MT.ImDrawList:CloneOutput()
local dst = ImDrawList(nil)
for i = 1, self.CmdBuffer.Size do
local src_cmd = self.CmdBuffer.Data[i]
local cmd = ImDrawCmd()
for k, v in pairs(src_cmd) do cmd[k] = v end
cmd.ClipRect = ImVec4(src_cmd.ClipRect.x, src_cmd.ClipRect.y, src_cmd.ClipRect.z, src_cmd.ClipRect.w)
if src_cmd.TexRef then cmd.TexRef = ImTextureRef(src_cmd.TexRef._TexID); cmd.TexRef._TexData = src_cmd.TexRef._TexData end
dst.CmdBuffer:push_back(cmd)
end
if self.IdxBuffer.Size > 0 then
dst.IdxBuffer:resize(self.IdxBuffer.Size)
table.move(self.IdxBuffer.Data, 1, self.IdxBuffer.Size, 1, dst.IdxBuffer.Data)
end
dst.VtxBuffer:resize(self.VtxBuffer.Size)
buffer.copy(dst.VtxBuffer.Buf, 0, self.VtxBuffer.Buf, 0, self.VtxBuffer.Size * VTX_STRIDE)
dst.Flags = self.Flags
return dst
end
function MT.ImDrawList:AddDrawCmd() function MT.ImDrawList:AddDrawCmd()
local draw_cmd = ImDrawCmd() local draw_cmd = ImDrawCmd()
@@ -4819,6 +4878,22 @@ function MT.ImDrawList:PrimRectUV(a, c, uv_a, uv_c, col)
self._IdxWritePtr = idx_write_ptr + 6 self._IdxWritePtr = idx_write_ptr + 6
end end
function MT.ImDrawList:PrimQuadUV(a, b, c, d, uv_a, uv_b, uv_c, uv_d, col)
local idx = self._VtxCurrentIdx
local idx_data = self.IdxBuffer.Data; local vtx_data = self.VtxBuffer.Buf
local idx_write_ptr = self._IdxWritePtr
idx_data[idx_write_ptr + 0] = idx; idx_data[idx_write_ptr + 1] = idx + 1; idx_data[idx_write_ptr + 2] = idx + 2
idx_data[idx_write_ptr + 3] = idx; idx_data[idx_write_ptr + 4] = idx + 2; idx_data[idx_write_ptr + 5] = idx + 3
local vtx_write_ptr = self._VtxWritePtr
WV(vtx_data, vtx_write_ptr + 0, a.x, a.y, uv_a.x, uv_a.y, col)
WV(vtx_data, vtx_write_ptr + 1, b.x, b.y, uv_b.x, uv_b.y, col)
WV(vtx_data, vtx_write_ptr + 2, c.x, c.y, uv_c.x, uv_c.y, col)
WV(vtx_data, vtx_write_ptr + 3, d.x, d.y, uv_d.x, uv_d.y, col)
self._VtxWritePtr = vtx_write_ptr + 4
self._VtxCurrentIdx = idx + 4
self._IdxWritePtr = idx_write_ptr + 6
end
--- @param thickness float --- @param thickness float
--- @return float --- @return float
-- [Luau] local function (also MT.ImDrawList._CalculateCenterBiasedOffset) -- [Luau] local function (also MT.ImDrawList._CalculateCenterBiasedOffset)
@@ -6808,6 +6883,30 @@ function MT.ImDrawList:AddImage(tex_ref, p_min, p_max, uv_min, uv_max, col)
end end
end end
function MT.ImDrawList:AddImageQuad(tex_ref, p1, p2, p3, p4, uv1, uv2, uv3, uv4, col)
if uv1 == nil then uv1 = ImVec2(0, 0) end
if uv2 == nil then uv2 = ImVec2(1, 0) end
if uv3 == nil then uv3 = ImVec2(1, 1) end
if uv4 == nil then uv4 = ImVec2(0, 1) end
if col == nil then col = IM_COL32_WHITE end
if bit32.band(col, IM_COL32_A_MASK) == 0 then
return
end
local push_texture_id = tex_ref ~= self._CmdHeader.TexRef
if push_texture_id then
self:PushTexture(tex_ref)
end
self:PrimReserve(6, 4)
self:PrimQuadUV(p1, p2, p3, p4, uv1, uv2, uv3, uv4, col)
if push_texture_id then
self:PopTexture()
end
end
--- @param tex_ref ImTextureRef --- @param tex_ref ImTextureRef
--- @param p_min ImVec2 --- @param p_min ImVec2
--- @param p_max ImVec2 --- @param p_max ImVec2
@@ -6901,6 +7000,9 @@ function MT.ImDrawList:PopClipRect()
self:_OnChangedClipRect() self:_OnChangedClipRect()
end end
function MT.ImDrawList:GetClipRectMin() local cr = self._ClipRectStack:back(); return ImVec2(cr.x, cr.y) end
function MT.ImDrawList:GetClipRectMax() local cr = self._ClipRectStack:back(); return ImVec2(cr.z, cr.w) end
--- @param center ImVec2 --- @param center ImVec2
--- @param radius float --- @param radius float
--- @param a_min_sample int --- @param a_min_sample int
+19 -8
View File
@@ -499,8 +499,8 @@ end
--- @field UserCallbackData any --- @field UserCallbackData any
--- @field UserCallbackDataSize int --- @field UserCallbackDataSize int
--- @field UserCallbackDataOffset int --- @field UserCallbackDataOffset int
local IM_DRAWCMD = {} MT.ImDrawCmd = {}
IM_DRAWCMD.__index = IM_DRAWCMD MT.ImDrawCmd.__index = MT.ImDrawCmd
--- @return ImDrawCmd --- @return ImDrawCmd
--- @nodiscard --- @nodiscard
@@ -515,11 +515,11 @@ function ImDrawCmd()
UserCallbackData = nil, -- 4-8 -- Callback user data (when UserCallback != nil). If called AddCallback() with size == 0, this is a copy of the AddCallback() argument. If called AddCallback() with size > 0, this is pointing to a buffer where data is stored. UserCallbackData = nil, -- 4-8 -- Callback user data (when UserCallback != nil). If called AddCallback() with size == 0, this is a copy of the AddCallback() argument. If called AddCallback() with size > 0, this is pointing to a buffer where data is stored.
UserCallbackDataSize = 0, -- 4 -- Size of callback user data when using storage, otherwise 0. UserCallbackDataSize = 0, -- 4 -- Size of callback user data when using storage, otherwise 0.
UserCallbackDataOffset = 0 -- 4 -- [Internal] Offset of callback user data when using storage, otherwise -1. UserCallbackDataOffset = 0 -- 4 -- [Internal] Offset of callback user data when using storage, otherwise -1.
}, IM_DRAWCMD) }, MT.ImDrawCmd)
end end
--- @return ImTextureID --- @return ImTextureID
function IM_DRAWCMD:GetTexID() function MT.ImDrawCmd:GetTexID()
local tex_id = (self.TexRef._TexData) and self.TexRef._TexData.TexID or self.TexRef._TexID local tex_id = (self.TexRef._TexData) and self.TexRef._TexData.TexID or self.TexRef._TexID
if self.TexRef._TexData ~= nil then if self.TexRef._TexData ~= nil then
IM_ASSERT(tex_id ~= ImTextureID_Invalid, "ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexID() after handling ImTextureStatus_WantCreate request!") IM_ASSERT(tex_id ~= ImTextureID_Invalid, "ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexID() after handling ImTextureStatus_WantCreate request!")
@@ -775,11 +775,14 @@ end
--- @param x int --- @param x int
--- @param y int --- @param y int
function MT.ImTextureData:GetPixels() IM_ASSERT(self.Pixels ~= nil); return self.Pixels end
function MT.ImTextureData:GetPixelsAt(x, y) function MT.ImTextureData:GetPixelsAt(x, y)
return self.Pixels, (x + y * self.Width) * self.BytesPerPixel + 1 return self.Pixels, (x + y * self.Width) * self.BytesPerPixel + 1
end end
function MT.ImTextureData:GetSizeInBytes() return self.Width * self.Height * self.BytesPerPixel end
function MT.ImTextureData:GetPitch() return self.Width * self.BytesPerPixel end function MT.ImTextureData:GetPitch() return self.Width * self.BytesPerPixel end
function MT.ImTextureData:GetTexRef() local tex_ref = ImTextureRef(); tex_ref._TexData = self; tex_ref._TexID = ImTextureID_Invalid; return tex_ref end
function MT.ImTextureData:GetTexID() return self.TexID end function MT.ImTextureData:GetTexID() return self.TexID end
--- @param tex_id ImTextureID --- @param tex_id ImTextureID
@@ -799,8 +802,8 @@ function MT.ImTextureData:SetStatus(status) self.Status = status if (status == I
--- @class ImTextureRef --- @class ImTextureRef
--- @field _TexData? ImTextureData --- @field _TexData? ImTextureData
--- @field _TexID ImTextureID --- @field _TexID ImTextureID
local IM_TEXTURE_REF = {} MT.ImTextureRef = {}
IM_TEXTURE_REF.__index = IM_TEXTURE_REF MT.ImTextureRef.__index = MT.ImTextureRef
--- @return ImTextureRef --- @return ImTextureRef
--- @nodiscard --- @nodiscard
@@ -809,7 +812,14 @@ function ImTextureRef(tex_id)
-- Members (either are set, never both!) -- Members (either are set, never both!)
_TexData = nil, -- A texture, generally owned by a ImFontAtlas. Will convert to ImTextureID during render loop, after texture has been uploaded. _TexData = nil, -- A texture, generally owned by a ImFontAtlas. Will convert to ImTextureID during render loop, after texture has been uploaded.
_TexID = tex_id or ImTextureID_Invalid -- _OR_ Low-level backend texture identifier, if already uploaded or created by user/app. Generally provided to e.g. ImGui.Image() calls. _TexID = tex_id or ImTextureID_Invalid -- _OR_ Low-level backend texture identifier, if already uploaded or created by user/app. Generally provided to e.g. ImGui.Image() calls.
}, IM_TEXTURE_REF) }, MT.ImTextureRef)
end
-- This is provided for consistency (but we don't actually use this)
--- @return ImTextureID
function MT.ImTextureRef:GetTexID()
IM_ASSERT(not (self._TexData ~= nil and self._TexID ~= ImTextureID_Invalid))
if self._TexData then return self._TexData.TexID else return self._TexID end
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@@ -2543,7 +2553,8 @@ function MT.ImGuiInputTextCallbackData:InsertChars(pos, new_text)
self.SelectionStart = self.CursorPos; self.SelectionEnd = self.CursorPos self.SelectionStart = self.CursorPos; self.SelectionEnd = self.CursorPos
end end
function MT.ImGuiInputTextCallbackData:SelectAll() self.SelectionStart = 0; self.SelectionEnd = self.BufTextLen end function MT.ImGuiInputTextCallbackData:SelectAll() self.SelectionStart = 0; self.CursorPos = self.BufTextLen; self.SelectionEnd = self.BufTextLen end
function MT.ImGuiInputTextCallbackData:SetSelection(s, e) IM_ASSERT(s >= 0 and s <= self.BufTextLen); IM_ASSERT(e >= 0 and e <= self.BufTextLen); self.SelectionStart = s; self.CursorPos = e; self.SelectionEnd = e end
function MT.ImGuiInputTextCallbackData:ClearSelection() self.SelectionStart = self.BufTextLen; 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 function MT.ImGuiInputTextCallbackData:HasSelection() return self.SelectionStart ~= self.SelectionEnd end
+183 -26
View File
@@ -706,6 +706,27 @@ function ImCharIsBlankA(c) return c == 32 or c == 9 end
--- @param c char --- @param c char
function ImCharIsBlankW(c) return c == 32 or c == 9 or c == 0x3000 end function ImCharIsBlankW(c) return c == 32 or c == 9 or c == 0x3000 end
--- @param c char
function ImCharIsXdigitA(c) return (c >= 48 and c <= 57) or (c >= 65 and c <= 70) or (c >= 97 and c <= 102) end
--- @param lhs ImVec2
--- @param fail_value float
function ImInvLength(lhs, fail_value) local d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if d > 0.0 then return ImRsqrt(d) end; return fail_value end
function ImRoundPositive64(f) return _Trunc(f + 0.5) end -- Positive values only.
function ImRoundSigned64(f) if f >= 0.0 then return _Trunc(f + 0.5) else return _Trunc(f - 0.5) end end
-- [Luau] C '%' truncates toward zero; math.fmod matches it
function ImModPositive(a, b) return math.fmod(a + b, b) end
function ImIsFloatAboveGuaranteedIntegerPrecision(f) return f <= -16777216 or f >= 16777216 end
-- Helper: ImVec2i (2D vector, integer) / ImVec2ih (2D vector, half-size integer, for long-term packed storage)
-- [Luau] both are plain ImVec2 holding whole numbers
function ImVec2i(x, y) return ImVec2(x or 0, y or 0) end
--- @overload fun(rhs: ImVec2): ImVec2
function ImVec2ih(x, y)
if type(x) == "table" then return ImVec2(_Trunc(x.x), _Trunc(x.y)) end
return ImVec2(x or 0, y or 0)
end
--- @enum ImGuiNavMoveFlags --- @enum ImGuiNavMoveFlags
ImGuiNavMoveFlags = ImGuiNavMoveFlags =
{ {
@@ -1004,6 +1025,28 @@ function ImBitArraySetBit(arr, n)
arr[idx] = bitOr(arr[idx], mask) arr[idx] = bitOr(arr[idx], mask)
end end
-- [Luau] bit arrays are ImU32[] tables with 1-based word indices; bit indices 'n' are 1-based like the functions above
--- @param bitcount int
function ImBitArrayGetStorageSizeInBytes(bitcount) return bitLShift(bitRShift(bitcount + 31, 5), 2) end
--- @param arr ImU32[]
--- @param bitcount int
function ImBitArrayClearAllBits(arr, bitcount) for i = 1, bitRShift(bitcount + 31, 5) do arr[i] = 0 end end
function ImBitArrayTestBit(arr, n) local mask = bitLShift(1, bitAnd(n - 1, 31)); return bitAnd(arr[bitRShift(n - 1, 5) + 1], mask) ~= 0 end
function ImBitArrayClearBit(arr, n) local mask = bitLShift(1, bitAnd(n - 1, 31)); local idx = bitRShift(n - 1, 5) + 1; arr[idx] = bitAnd(arr[idx], bitNot(mask)) end
-- Works on range [n..n2)
function ImBitArraySetBitRange(arr, n, n2)
n = n - 1; n2 = n2 - 1 -- [Luau] to 0-based
n2 = n2 - 1
while n <= n2 do
local a_mod = bitAnd(n, 31)
local b_mod = ((n2 > bitOr(n, 31)) and 31 or bitAnd(n2, 31)) + 1
local mask = bitAnd(2 ^ b_mod - 1, bitNot(2 ^ a_mod - 1))
local idx = bitRShift(n, 5) + 1
arr[idx] = bitOr(arr[idx], mask)
n = bitAnd(n + 32, bitNot(31))
end
end
-- Bit array for named keys -- Bit array for named keys
--- @alias ImBitArrayForNamedKeys ImBitArray --- @alias ImBitArrayForNamedKeys ImBitArray
@@ -1030,11 +1073,30 @@ function ImBitArray(BITCOUNT, OFFSET)
--- @param n int # 1-based --- @param n int # 1-based
this.SetBit = function(self, n) n = n + OFFSET; IM_ASSERT(n >= 1 and n <= BITCOUNT); ImBitArraySetBit(self.Data, n); end this.SetBit = function(self, n) n = n + OFFSET; IM_ASSERT(n >= 1 and n <= BITCOUNT); ImBitArraySetBit(self.Data, n); end
--- @param n int # 1-based
this.ClearBit = function(self, n) n = n + OFFSET; IM_ASSERT(n >= 1 and n <= BITCOUNT); ImBitArrayClearBit(self.Data, n); end
--- @param n int # 1-based, range [n..n2)
this.SetBitRange = function(self, n, n2) n = n + OFFSET; n2 = n2 + OFFSET; IM_ASSERT(n >= 1 and n <= BITCOUNT and n2 > n and n2 <= BITCOUNT + 1); ImBitArraySetBitRange(self.Data, n, n2); end
this:ClearAllBits() this:ClearAllBits()
return this return this
end end
-- Helper: ImBitVector
-- Store 1-bit per value.
--- @class ImBitVector
--- @field Storage ImVector<ImU32>
MT.ImBitVector = {}
MT.ImBitVector.__index = MT.ImBitVector
function ImBitVector() return setmetatable({ Storage = ImVector() }, MT.ImBitVector) end
function MT.ImBitVector:Create(sz) self.Storage:resize(bitRShift(sz + 31, 5)); for i = 1, self.Storage.Size do self.Storage.Data[i] = 0 end end
function MT.ImBitVector:Clear() self.Storage:clear() end
--- @param n int # 1-based
function MT.ImBitVector:TestBit(n) IM_ASSERT(n <= bitLShift(self.Storage.Size, 5)); return IM_BITARRAY_TESTBIT(self.Storage.Data, n) end
function MT.ImBitVector:SetBit(n) IM_ASSERT(n <= bitLShift(self.Storage.Size, 5)); ImBitArraySetBit(self.Storage.Data, n) end
function MT.ImBitVector:ClearBit(n) IM_ASSERT(n <= bitLShift(self.Storage.Size, 5)); ImBitArrayClearBit(self.Storage.Data, n) end
-- Stateful path API, add points then finish with PathFillConvex() or PathStroke() -- Stateful path API, add points then finish with PathFillConvex() or PathStroke()
-- - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. -- - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
-- so e.g. 'PathArcTo(center, radius, PI * -0.5, PI)' is ok, whereas 'PathArcTo(center, radius, PI, PI * -0.5)' won't have correct anti-aliasing when followed by PathFillConvex(). -- so e.g. 'PathArcTo(center, radius, PI * -0.5, PI)' is ok, whereas 'PathArcTo(center, radius, PI, PI * -0.5)' won't have correct anti-aliasing when followed by PathFillConvex().
@@ -1266,9 +1328,13 @@ end
--- @param key string --- @param key string
--- @return ImGuiStyleVarInfo --- @return ImGuiStyleVarInfo
--- @nodiscard --- @nodiscard
MT.ImGuiStyleVarInfo = {}
MT.ImGuiStyleVarInfo.__index = MT.ImGuiStyleVarInfo
function ImGuiStyleVarInfo(count, data_type, key) function ImGuiStyleVarInfo(count, data_type, key)
return { Count = count, DataType = data_type, Key = key } return setmetatable({ Count = count, DataType = data_type, Key = key }, MT.ImGuiStyleVarInfo)
end end
-- [Luau] no pointers: returns the field value (number, or the live ImVec2 table) of 'parent' (e.g. ImGuiStyle)
function MT.ImGuiStyleVarInfo:GetVarPtr(parent) return parent[self.Key] end
-- Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. -- Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable.
--- @class ImGuiStyleMod --- @class ImGuiStyleMod
@@ -1383,11 +1449,11 @@ end
--- @field RefVal any --- @field RefVal any
--- @field StorageId ImGuiID --- @field StorageId ImGuiID
--- @field ColorMarker ImU32 --- @field ColorMarker ImU32
local IMGUI_NEXT_ITEM_DATA = {} MT.ImGuiNextItemData = {}
IMGUI_NEXT_ITEM_DATA.__index = IMGUI_NEXT_ITEM_DATA MT.ImGuiNextItemData.__index = MT.ImGuiNextItemData
-- Also cleared manually by ItemAdd()! -- Also cleared manually by ItemAdd()!
function IMGUI_NEXT_ITEM_DATA:ClearFlags() function MT.ImGuiNextItemData:ClearFlags()
self.HasFlags = ImGuiNextItemDataFlags.None self.HasFlags = ImGuiNextItemDataFlags.None
self.ItemFlagsSet = ImGuiItemFlags.None self.ItemFlagsSet = ImGuiItemFlags.None
end end
@@ -1410,7 +1476,7 @@ function ImGuiNextItemData()
RefVal = 0, -- Not exposed yet, for ImGuiInputTextFlags.ParseEmptyAsRefVal RefVal = 0, -- Not exposed yet, for ImGuiInputTextFlags.ParseEmptyAsRefVal
StorageId = 0, -- Set by SetNextItemStorageID() StorageId = 0, -- Set by SetNextItemStorageID()
ColorMarker = 0 -- Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags.ColorMarkers. ColorMarker = 0 -- Set by SetNextItemColorMarker(). Not exposed yet, supported by DragScalar,SliderScalar and for ImGuiSliderFlags.ColorMarkers.
}, IMGUI_NEXT_ITEM_DATA) }, MT.ImGuiNextItemData)
end end
-- Storage for SetNexWindow** functions -- Storage for SetNexWindow** functions
@@ -1433,10 +1499,10 @@ end
--- @field BgAlphaVal number? --- @field BgAlphaVal number?
--- @field MenuBarOffsetMinVal ImVec2? --- @field MenuBarOffsetMinVal ImVec2?
--- @field RefreshFlagsVal int? --- @field RefreshFlagsVal int?
local IMGUI_NEXT_WINDOW_DATA = {} MT.ImGuiNextWindowData = {}
IMGUI_NEXT_WINDOW_DATA.__index = IMGUI_NEXT_WINDOW_DATA MT.ImGuiNextWindowData.__index = MT.ImGuiNextWindowData
function IMGUI_NEXT_WINDOW_DATA:ClearFlags() function MT.ImGuiNextWindowData:ClearFlags()
self.HasFlags = ImGuiNextWindowDataFlags.None self.HasFlags = ImGuiNextWindowDataFlags.None
end end
@@ -1475,7 +1541,7 @@ function ImGuiNextWindowData()
RefreshFlagsVal = nil, RefreshFlagsVal = nil,
CacheKeySet = false, -- [Port] SetNextWindowCached() CacheKeySet = false, -- [Port] SetNextWindowCached()
CacheKeyVal = nil CacheKeyVal = nil
}, IMGUI_NEXT_WINDOW_DATA) }, MT.ImGuiNextWindowData)
end end
--- @enum ImGuiWindowBgClickFlags --- @enum ImGuiWindowBgClickFlags
@@ -1738,21 +1804,29 @@ local function ImGuiInputTextState()
SelectedAllMouseLock = false, -- after a double-click to select all, we ignore further mouse drags to update selection SelectedAllMouseLock = false, -- after a double-click to select all, we ignore further mouse drags to update selection
EditedBefore = false, -- edited since activated EditedBefore = false, -- edited since activated
EditedThisFrame = false, -- edited this frame EditedThisFrame = false, -- edited this frame
WantReloadUserBuf = false, -- force a reload of user buf so it may be modified externally. may be automatic in future version.
LastMoveDirectionLR = ImGuiDir.Left, -- ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s
ReloadSelectionStart = 0,
ReloadSelectionEnd = 0,
} }
return setmetatable(this, MT.ImGuiInputTextState) return setmetatable(this, MT.ImGuiInputTextState)
end end
function MT.ImGuiInputTextState:ClearText() self.TextLen = 0; self.TextA.Data[1] = 0; self:CursorClamp() end
function MT.ImGuiInputTextState:ClearFreeMemory() self.TextA:clear(); self.TextToRevertTo:clear() end
function MT.ImGuiInputTextState:GetText() if self.TextA.Data then return self.TextA.Data else return { 0 } end end -- [Luau] byte table (char[])
-- Internal temporary state for deactivating InputText() instances. -- Internal temporary state for deactivating InputText() instances.
-- Store as part of ImGuiDeactivatedItemData? -- Store as part of ImGuiDeactivatedItemData?
--- @class ImGuiInputTextDeactivatedState --- @class ImGuiInputTextDeactivatedState
--- @field ID ImGuiID # widget id owning the text state (which just got deactivated) --- @field ID ImGuiID # widget id owning the text state (which just got deactivated)
--- @field ElapseFrame int --- @field ElapseFrame int
--- @field TextA ImVector<char> # text buffer --- @field TextA ImVector<char> # text buffer
local IMGUI_INPUT_TEXT_DEACTIVATED_STATE = {} MT.ImGuiInputTextDeactivatedState = {}
IMGUI_INPUT_TEXT_DEACTIVATED_STATE.__index = IMGUI_INPUT_TEXT_DEACTIVATED_STATE MT.ImGuiInputTextDeactivatedState.__index = MT.ImGuiInputTextDeactivatedState
function IMGUI_INPUT_TEXT_DEACTIVATED_STATE:ClearFreeMemory() function MT.ImGuiInputTextDeactivatedState:ClearFreeMemory()
self.ID = 0 self.ID = 0
self.ElapseFrame = 0 self.ElapseFrame = 0
self.TextA:clear() self.TextA:clear()
@@ -1765,7 +1839,7 @@ local function ImGuiInputTextDeactivatedState()
ID = 0, -- widget id owning the text state (which just got deactivated) ID = 0, -- widget id owning the text state (which just got deactivated)
ElapseFrame = 0, ElapseFrame = 0,
TextA = ImVector() -- text buffer TextA = ImVector() -- text buffer
}, IMGUI_INPUT_TEXT_DEACTIVATED_STATE) }, MT.ImGuiInputTextDeactivatedState)
end end
--- @alias ImGuiKeyRoutingIndex ImS16 --- @alias ImGuiKeyRoutingIndex ImS16
@@ -1798,10 +1872,10 @@ end
--- @field Index ImGuiKeyRoutingIndex[] # Index of first entry in Entries[] --- @field Index ImGuiKeyRoutingIndex[] # Index of first entry in Entries[]
--- @field Entries ImVector<ImGuiKeyRoutingData> --- @field Entries ImVector<ImGuiKeyRoutingData>
--- @field EntriesNext ImVector<ImGuiKeyRoutingData> # Double-buffer to avoid reallocation (could use a shared buffer) --- @field EntriesNext ImVector<ImGuiKeyRoutingData> # Double-buffer to avoid reallocation (could use a shared buffer)
local _ImGuiKeyRoutingTable = {} MT.ImGuiKeyRoutingTable = {}
_ImGuiKeyRoutingTable.__index = _ImGuiKeyRoutingTable MT.ImGuiKeyRoutingTable.__index = MT.ImGuiKeyRoutingTable
function _ImGuiKeyRoutingTable:Clear() function MT.ImGuiKeyRoutingTable:Clear()
for n = 1, ImGuiKey.NamedKey_COUNT do self.Index[n] = -1 end for n = 1, ImGuiKey.NamedKey_COUNT do self.Index[n] = -1 end
self.Entries:clear() self.Entries:clear()
self.EntriesNext:clear() self.EntriesNext:clear()
@@ -1813,7 +1887,7 @@ local function ImGuiKeyRoutingTable()
Index = {}, -- Index of first entry in Entries[] Index = {}, -- Index of first entry in Entries[]
Entries = ImVector(), Entries = ImVector(),
EntriesNext = ImVector() -- Double-buffer to avoid reallocation (could use a shared buffer) EntriesNext = ImVector() -- Double-buffer to avoid reallocation (could use a shared buffer)
}, _ImGuiKeyRoutingTable) }, MT.ImGuiKeyRoutingTable)
this:Clear() this:Clear()
@@ -1899,10 +1973,10 @@ end
--- @field DistBox float # Move -- Best candidate box distance to current NavId --- @field DistBox float # Move -- Best candidate box distance to current NavId
--- @field DistCenter float # Move -- Best candidate center distance to current NavId --- @field DistCenter float # Move -- Best candidate center distance to current NavId
--- @field DistAxial float # Move -- Best candidate axial distance to current NavId --- @field DistAxial float # Move -- Best candidate axial distance to current NavId
local IMGUI_NAV_ITEM_DATA = {} MT.ImGuiNavItemData = {}
IMGUI_NAV_ITEM_DATA.__index = IMGUI_NAV_ITEM_DATA MT.ImGuiNavItemData.__index = MT.ImGuiNavItemData
function IMGUI_NAV_ITEM_DATA:Clear() function MT.ImGuiNavItemData:Clear()
self.Window = nil self.Window = nil
self.ID = 0 self.ID = 0
self.FocusScopeId = 0 self.FocusScopeId = 0
@@ -1915,7 +1989,7 @@ end
--- @return ImGuiNavItemData --- @return ImGuiNavItemData
local function ImGuiNavItemData() local function ImGuiNavItemData()
local this = setmetatable({ }, IMGUI_NAV_ITEM_DATA) local this = setmetatable({ }, MT.ImGuiNavItemData)
this.RectRel = ImRect() this.RectRel = ImRect()
this:Clear() this:Clear()
return this return this
@@ -2488,7 +2562,7 @@ function ImGuiContext(shared_font_atlas) -- TODO: tidy up / complete this struct
DockContext = ImGuiDockContext(), DockContext = ImGuiDockContext(),
DockNodeWindowMenuHandler = nil, DockNodeWindowMenuHandler = nil,
DebugHoveredDockNode = nil, -- Hovered dock node. DebugHoveredDockNode = nil, -- Hovered dock node.
CurrentTabBarStack = ImVector(), -- holds ImGuiTabBar references directly (no ImGuiPtrOrIndex needed in Lua) CurrentTabBarStack = ImVector(), -- ImVector<ImGuiPtrOrIndex>
ShrinkWidthBuffer = ImVector(), -- ImGuiShrinkWidthItem ShrinkWidthBuffer = ImVector(), -- ImGuiShrinkWidthItem
-- Multi-Select state -- Multi-Select state
@@ -2652,10 +2726,10 @@ end
--- @field WantApply bool # Set when loaded from .ini data --- @field WantApply bool # Set when loaded from .ini data
--- @field WantDelete bool # Set to invalidate/delete the settings entry --- @field WantDelete bool # Set to invalidate/delete the settings entry
--- @field Name string # Window name --- @field Name string # Window name
local IMGUI_WINDOW_SETTINGS = {} MT.ImGuiWindowSettings = {}
IMGUI_WINDOW_SETTINGS.__index = IMGUI_WINDOW_SETTINGS MT.ImGuiWindowSettings.__index = MT.ImGuiWindowSettings
function IMGUI_WINDOW_SETTINGS:GetName() function MT.ImGuiWindowSettings:GetName()
return self.Name return self.Name
end end
@@ -2675,7 +2749,7 @@ function ImGuiWindowSettings()
DockOrder = -1, -- Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. DockOrder = -1, -- Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible.
ClassId = 0, -- ID of window class if specified ClassId = 0, -- ID of window class if specified
LastUsedDate = 0, LastUsedDate = 0,
}, IMGUI_WINDOW_SETTINGS) }, MT.ImGuiWindowSettings)
end end
-- Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. -- Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper.
@@ -3391,6 +3465,8 @@ function ImGuiInputEvent()
end end
-- [Internal] Key ranges -- [Internal] Key ranges
ImGuiKey_LegacyNativeKey_BEGIN = 0
ImGuiKey_LegacyNativeKey_END = 512
ImGuiKey_Keyboard_BEGIN = ImGuiKey.NamedKey_BEGIN ImGuiKey_Keyboard_BEGIN = ImGuiKey.NamedKey_BEGIN
ImGuiKey_Keyboard_END = ImGuiKey.GamepadStart ImGuiKey_Keyboard_END = ImGuiKey.GamepadStart
ImGuiKey_Gamepad_BEGIN = ImGuiKey.GamepadStart ImGuiKey_Gamepad_BEGIN = ImGuiKey.GamepadStart
@@ -3715,5 +3791,86 @@ end
--- @nodiscard --- @nodiscard
function ImGuiFocusScopeData() return { ID = nil, WindowID = nil } end function ImGuiFocusScopeData() return { ID = nil, WindowID = nil } end
-------------------------------------------------------------------------------
-- [Luau] Inline struct helpers / constructors from imgui.h + imgui_internal.h
-------------------------------------------------------------------------------
-- Helper for running a block of code not more than once a frame
-- [Luau] no operator bool(): call the object, e.g. 'if once() then ... end'
MT.ImGuiOnceUponAFrame = {}
MT.ImGuiOnceUponAFrame.__index = MT.ImGuiOnceUponAFrame
MT.ImGuiOnceUponAFrame.__call = function(self) local current_frame = ImGui.GetFrameCount(); if self.RefFrame == current_frame then return false end; self.RefFrame = current_frame; return true end
function ImGuiOnceUponAFrame() return setmetatable({ RefFrame = -1 }, MT.ImGuiOnceUponAFrame) end
-- [Internal] ImGuiTextFilter item
-- [Luau] stores the item text as a Lua string (upstream: Begin pointer + Len)
--- @param buf string
--- @param b int # 1-based
--- @param e int # exclusive
function ImGuiTextFilterItem(buf, b, e) IM_ASSERT(e > b); return { Text = string.sub(buf, b, e - 1), CountInclude = 0, MatchIncludesMask = 0 } end
-- A date in YYYYMMDD format packed into 16-bits
--- @class ImGuiPackedDate
MT.ImGuiPackedDate = {}
MT.ImGuiPackedDate.__index = MT.ImGuiPackedDate
--- @param yyyymmdd? int
function ImGuiPackedDate(yyyymmdd)
local this = setmetatable({ Year = 0, Month = 0, Day = 0 }, MT.ImGuiPackedDate)
if yyyymmdd ~= nil then -- Pack
this.Year = bitAnd(math.floor(yyyymmdd / 10000) - 2000, 0x7F) -- Year since 2000 (7 bits)
this.Month = bitAnd(math.floor(yyyymmdd / 100) % 100, 0x0F) -- Month (1-12) (4 bits)
this.Day = bitAnd(yyyymmdd % 100, 0x1F) -- Day (1-31) (5 bits)
end
return this
end
function MT.ImGuiPackedDate:IsValid() return self.Year ~= 0 and self.Month ~= 0 and self.Day ~= 0 end
function MT.ImGuiPackedDate:Unpack() if self.Year ~= 0 and self.Month ~= 0 and self.Day ~= 0 then return ((self.Year + 2000) * 10000) + (self.Month * 100) + self.Day end; return 0 end -- Unpack
function MT.ImGuiPackedDate:SubtractMonths(m) while m > 0 do if self.Month == 1 then self.Year = bitAnd(self.Year - 1, 0x7F) end; self.Month = (self.Month == 1) and 12 or self.Month - 1; m = m - 1 end end -- FIXME-OPT: Stupid but enough for what we do with it.
-- Pointer or index, e.g. g.CurrentTabBarStack[]
-- [Luau] overload by argument type: number = index, otherwise pointer
function ImGuiPtrOrIndex(ptr_or_index)
if type(ptr_or_index) == "number" then return { Ptr = nil, Index = ptr_or_index } end
return { Ptr = ptr_or_index, Index = -1 }
end
-- Note that Max is exclusive, so perhaps should be using a Begin/End convention.
--- @class ImGuiListClipperRange
MT.ImGuiListClipperRange = {}
function MT.ImGuiListClipperRange.FromIndices(min, max) return { Min = min, Max = max, PosToIndexConvert = false, PosToIndexOffsetMin = 0, PosToIndexOffsetMax = 0 } end
function MT.ImGuiListClipperRange.FromPositions(y1, y2, off_min, off_max) return { Min = _Trunc(y1), Max = _Trunc(y2), PosToIndexConvert = true, PosToIndexOffsetMin = off_min, PosToIndexOffsetMax = off_max } end
-- Temporary clipper data, buffers shared/reused between instances
--- @class ImGuiListClipperData
MT.ImGuiListClipperData = {}
MT.ImGuiListClipperData.__index = MT.ImGuiListClipperData
function ImGuiListClipperData() return setmetatable({ ListClipper = nil, LossynessOffset = 0.0, StepNo = 0, ItemsFrozen = 0, Ranges = ImVector() }, MT.ImGuiListClipperData) end
function MT.ImGuiListClipperData:Reset(clipper) self.ListClipper = clipper; self.StepNo = 0; self.ItemsFrozen = 0; self.Ranges:resize(0) end
function ImGuiStackLevelInfo() return { ID = 0, QueryFrameCount = 0, QuerySuccess = false, DataType = -1, DescOffset = -1 } end
function ImGuiDebugItemPathQuery() return { MainID = 0, Active = false, Complete = false, Step = 0, Results = ImVector(), ResultsDescBuf = ImGuiTextBuffer(), ResultPathBuf = ImGuiTextBuffer() } end
function ImGuiIDStackTool() return { OptHexEncodeNonAsciiChars = true, OptCopyToClipboardOnCtrlC = false, LastActiveFrame = -1, CopyToClipboardLastTime = -FLT_MAX } end
function ImGuiContextHook() return { HookId = 0, Type = 0, Owner = 0, Callback = nil, UserData = nil } end
-------------------------------------------------------------------------------
-- [Luau] ImGui:: inline helpers from imgui_internal.h
-------------------------------------------------------------------------------
function ImGui.GetCurrentTable() local g = GImGui; return g.CurrentTable end
--- @param window ImGuiWindow
--- @param parent_window ImGuiWindow?
function ImGui.SetWindowParentWindowForFocusRoute(window, parent_window) window.ParentWindowForFocusRoute = parent_window end -- You may also use SetNextWindowClass()'s FocusRouteParentWindowId field.
function ImGui.GetPixelDensity() return GImGui.FontRasterizerDensity end -- [Luau] g.FontRasterizerDensity == upstream g.CurrentPixelDensity
function ImGui.ScrollToBringRectIntoView(window, rect) ImGui.ScrollToRect(window, rect, ImGuiScrollFlags.KeepVisibleEdgeY) end
function ImGui.GetItemStatusFlags() local g = GImGui; return g.LastItemData.StatusFlags end
function ImGui.GetActiveID() local g = GImGui; return g.ActiveId end
function ImGui.GetFocusID() local g = GImGui; return g.NavId end
function ImGui.IsLegacyKey(key) return key >= ImGuiKey_LegacyNativeKey_BEGIN and key < ImGuiKey_LegacyNativeKey_END end
function ImGui.GetWindowDockNode() local g = GImGui; return g.CurrentWindow.DockNode end
function ImGui.GetCurrentFocusScope() local g = GImGui; return g.CurrentFocusScopeId end -- Focus scope we are outputting into, set by PushFocusScope()
function ImGui.GetCurrentTabBar() local g = GImGui; return g.CurrentTabBar end
-- This may be useful to apply workaround that a based on distinguish whenever an item is active as a text input field.
function ImGui.IsItemActiveAsInputText() local g = GImGui; return g.ActiveId ~= 0 and g.ActiveId == g.LastItemData.ID and g.InputTextState.ID == g.LastItemData.ID end
return true -- [Roblox] ModuleScripts must return exactly one value return true -- [Roblox] ModuleScripts must return exactly one value
+53 -12
View File
@@ -331,9 +331,13 @@ end
-- Storage for a table .ini settings -- Storage for a table .ini settings
--- @class ImGuiTableSettings --- @class ImGuiTableSettings
MT.ImGuiTableSettings = {}
MT.ImGuiTableSettings.__index = MT.ImGuiTableSettings
function ImGuiTableSettings() function ImGuiTableSettings()
return { ID = 0, SaveFlags = 0, RefScale = 0.0, ColumnsCount = 0, ColumnsCountMax = 0, LastUsedDate = 0, WantApply = false, Columns = {} } return setmetatable({ ID = 0, SaveFlags = 0, RefScale = 0.0, ColumnsCount = 0, ColumnsCountMax = 0, LastUsedDate = 0, WantApply = false, Columns = {} }, MT.ImGuiTableSettings)
end end
-- [Luau] column settings are stored in a 0-based .Columns table (upstream: allocated right after the struct)
function MT.ImGuiTableSettings:GetColumnSettings() return self.Columns end
-- Storage data for a single column for legacy Columns() api -- Storage data for a single column for legacy Columns() api
--- @class ImGuiOldColumnData --- @class ImGuiOldColumnData
@@ -4394,6 +4398,17 @@ function ImGui.TableLoadSettingsForColumn(column, column_settings, load_flags)
column.SortDirection = column_settings.SortDirection column.SortDirection = column_settings.SortDirection
end end
-- [Luau] boolean "less than" for table.sort; takes (table, lhs column idx, rhs column idx) instead of ImGuiTableFixDisplayOrderColumnData
local function TableFixDisplayOrderComparer(table_, lhs_idx, rhs_idx)
-- Assume ImGuiTableColumnIdx == signed short, to turn -1 into a large value so that it always sort after.
local lhs_order, rhs_order = table_.Columns[lhs_idx].DisplayOrder, table_.Columns[rhs_idx].DisplayOrder
if lhs_order < 0 then lhs_order = lhs_order + 0x10000 end
if rhs_order < 0 then rhs_order = rhs_order + 0x10000 end
local order_delta = lhs_order - rhs_order
if order_delta ~= 0 then return order_delta < 0 end
return lhs_idx < rhs_idx
end
-- Fix invalid display order data: compact values (0,1,3 -> 0,1,2); preserve relative order (0,3,1 -> 0,2,1); deduplicate (0,4,1,1 -> 0,3,1,2) -- Fix invalid display order data: compact values (0,1,3 -> 0,1,2); preserve relative order (0,3,1 -> 0,2,1); deduplicate (0,4,1,1 -> 0,3,1,2)
function ImGui.TableFixDisplayOrder(table_) function ImGui.TableFixDisplayOrder(table_)
if band(table_.Flags, ImGuiTableFlags.Reorderable) == 0 then if band(table_.Flags, ImGuiTableFlags.Reorderable) == 0 then
@@ -4408,17 +4423,7 @@ function ImGui.TableFixDisplayOrder(table_)
fdo_columns[n + 1] = n fdo_columns[n + 1] = n
end end
-- Sort by DisplayOrder and then Index -- Sort by DisplayOrder and then Index
-- Assume ImGuiTableColumnIdx == signed short, to turn -1 into a large value so that it always sort after. table.sort(fdo_columns, function(lhs_idx, rhs_idx) return TableFixDisplayOrderComparer(table_, lhs_idx, rhs_idx) end)
local function key(idx)
local o = table_.Columns[idx].DisplayOrder
if o < 0 then o = o + 0x10000 end
return o
end
table.sort(fdo_columns, function(lhs_idx, rhs_idx)
local a, b = key(lhs_idx), key(rhs_idx)
if a ~= b then return a < b end
return lhs_idx < rhs_idx
end)
for n = 0, table_.ColumnsCount - 1 do for n = 0, table_.ColumnsCount - 1 do
table_.Columns[fdo_columns[n + 1]].DisplayOrder = n table_.Columns[fdo_columns[n + 1]].DisplayOrder = n
end end
@@ -4427,6 +4432,27 @@ function ImGui.TableFixDisplayOrder(table_)
end end
end end
local function TableSettingsHandler_Cleanup(ctx, handler, args)
local g = ctx
for i = 0, g.Tables:GetMapSize() - 1 do
local table_ = g.Tables:GetByIndex(i)
if table_ then
table_.SettingsOffset = -1
end
end
-- [Luau] settings.LastUsedDate is stored unpacked (YYYYMMDD int, 0 = none); g.SessionDate is an ImGuiPackedDate when set
for _, settings in g.SettingsTables:iter() do
local is_valid = ImGuiPackedDate(settings.LastUsedDate):IsValid()
if args._DiscardOlderThanDate ~= 0 and settings.LastUsedDate < args._DiscardOlderThanDate then
settings.ID = 0
elseif args.DiscardWhenMissingDate and not is_valid then
settings.ID = 0
elseif args.SetCurrentSessionDateToAll or (args.SetCurrentSessionDateWhenMissingDate and not is_valid) then
settings.LastUsedDate = g.SessionDate and g.SessionDate:Unpack() or 0
end
end
end
local function ForEachTable(g, fn) local function ForEachTable(g, fn)
for i = 0, g.Tables:GetMapSize() - 1 do for i = 0, g.Tables:GetMapSize() - 1 do
local t = g.Tables:GetByIndex(i) local t = g.Tables:GetByIndex(i)
@@ -4552,6 +4578,7 @@ function ImGui.TableSettingsAddSettingsHandler()
ini_handler.ReadLineFn = TableSettingsHandler_ReadLine ini_handler.ReadLineFn = TableSettingsHandler_ReadLine
ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll
ini_handler.WriteAllFn = TableSettingsHandler_WriteAll ini_handler.WriteAllFn = TableSettingsHandler_WriteAll
ini_handler.CleanupFn = TableSettingsHandler_Cleanup
ImGui.AddSettingsHandler(ini_handler) ImGui.AddSettingsHandler(ini_handler)
end end
@@ -4615,6 +4642,20 @@ function ImGui.TableGcCompactSettings()
ForEachTable(g, function(t) t.SettingsOffset = -1 end) ForEachTable(g, function(t) t.SettingsOffset = -1 end)
end end
-------------------------------------------------------------------------------
-- [SECTION] Tables: Debugging
-------------------------------------------------------------------------------
-- [Luau] global (upstream: static) so ImGui.DebugNodeTable() in imgui.lua can use it
function DebugNodeTableGetSizingPolicyDesc(sizing_policy)
sizing_policy = band(sizing_policy, ImGuiTableFlags.SizingMask_)
if sizing_policy == ImGuiTableFlags.SizingFixedFit then return "FixedFit" end
if sizing_policy == ImGuiTableFlags.SizingFixedSame then return "FixedSame" end
if sizing_policy == ImGuiTableFlags.SizingStretchProp then return "StretchProp" end
if sizing_policy == ImGuiTableFlags.SizingStretchSame then return "StretchSame" end
return "N/A"
end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- [SECTION] Columns, BeginColumns, EndColumns, etc. -- [SECTION] Columns, BeginColumns, EndColumns, etc.
-- (This is a legacy API, prefer using BeginTable/EndTable!) -- (This is a legacy API, prefer using BeginTable/EndTable!)
+180 -42
View File
@@ -2598,12 +2598,14 @@ function ImGui.RoundScalarWithFormatT(format, data_type, v)
return v -- Don't apply if the value is not visible in the format string return v -- Don't apply if the value is not visible in the format string
end end
-- TODO: Sanitize format -- Sanitize format
local fmt_sanitized = ImParseFormatSanitizeForPrinting(string.sub(format, fmt_start), nil, 32)
-- Format value with our rounding, and read back -- Format value with our rounding, and read back
local str = ImFormatString(format, v) local v_str = ImFormatString(fmt_sanitized, v)
-- [Luau] leading spaces: ImAtof() skips them
v = ImAtof(str) or v v = ImAtof(v_str) or v
return v return v
end end
@@ -2689,6 +2691,30 @@ local function ImParseFormatTrimDecorations(fmt)
return string.sub(fmt, fmt_start, fmt_end - 1) return string.sub(fmt, fmt_start, fmt_end - 1)
end end
-- Sanitize format
-- - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi
-- - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.
-- [Luau] fmt_out is a byte table (may be nil); returns the sanitized format as a string
--- @param fmt_in string
--- @param fmt_out char[]?
--- @param fmt_out_size size_t
function ImParseFormatSanitizeForPrinting(fmt_in, fmt_out, fmt_out_size)
local fmt_end = ImParseFormatFindEnd(fmt_in, 1)
-- IM_UNUSED(fmt_out_size)
IM_ASSERT(fmt_end < fmt_out_size) -- Format is too long, let us know if this happens to you!
fmt_out = fmt_out or {}
local n = 0
for i = 1, fmt_end - 1 do
local c = string.byte(fmt_in, i)
if c ~= 39 and c ~= 36 and c ~= 95 then -- Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
n = n + 1
fmt_out[n] = c
end
end
fmt_out[n + 1] = 0 -- Zero-terminate
return string.char(unpack(fmt_out, 1, n))
end
-- - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d" -- - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d"
--- @param fmt_in string --- @param fmt_in string
--- @param fmt_out char[] --- @param fmt_out char[]
@@ -3290,6 +3316,18 @@ function ImGui.DragBehaviorT(TYPE, SIGNEDTYPE, data_type, v, v_speed, v_min, v_m
return v, true return v, true
end end
local function ShortcutsForCancel(id)
local g = GImGui
local is_cancel_with_keyboard = ImGui.Shortcut(ImGuiKey.Escape, ImGuiInputFlags.None, id)
-- [Luau] ImGuiKey_NavGamepadCancel macro inlined (io.ConfigNavSwapGamepadButtons is not ported: always FaceRight)
local is_cancel_with_gamepad = bit32.band(g.IO.ConfigFlags, ImGuiConfigFlags.NavEnableGamepad) ~= 0 and bit32.band(g.IO.BackendFlags, ImGuiBackendFlags.HasGamepad) ~= 0 and ImGui.Shortcut(ImGuiKey.GamepadFaceRight, ImGuiInputFlags.None, id)
local is_cancel_with_mouse = ImGui.IsMouseReleased(ImGuiMouseButton.Right, id)
if is_cancel_with_mouse then
ImGui.SetKeyOwner(ImGuiKey.MouseRight, id)
end
return is_cancel_with_keyboard or is_cancel_with_gamepad or is_cancel_with_mouse
end
--- @param id ImGuiID --- @param id ImGuiID
--- @param data_type ImGuiDataType --- @param data_type ImGuiDataType
--- @param v number --- @param v number
@@ -3311,6 +3349,13 @@ function ImGui.DragBehavior(id, data_type, v, v_speed, min, max, format, flags)
elseif (g.ActiveIdSource == ImGuiInputSource.Keyboard or g.ActiveIdSource == ImGuiInputSource.Gamepad) and g.NavActivatePressedId == id and not g.ActiveIdIsJustActivated then elseif (g.ActiveIdSource == ImGuiInputSource.Keyboard or g.ActiveIdSource == ImGuiInputSource.Gamepad) and g.NavActivatePressedId == id and not g.ActiveIdIsJustActivated then
ImGui.ClearActiveID() ImGui.ClearActiveID()
end end
if ShortcutsForCancel(id) and g.ActiveId == id then
-- Canceling action reverts to initial value
local value_changed = (v ~= g.ActiveIdValueOnActivation)
v = g.ActiveIdValueOnActivation
ImGui.ClearActiveID()
return v, value_changed
end
end end
if g.ActiveId ~= id then if g.ActiveId ~= id then
return v, false return v, false
@@ -3869,9 +3914,7 @@ function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_
local set_new_value = false local set_new_value = false
local clicked_t = 0.0 local clicked_t = 0.0
if g.ActiveIdSource == ImGuiInputSource.Mouse then if g.ActiveIdSource == ImGuiInputSource.Mouse then
if not g.IO.MouseDown[0] then do
ImGui.ClearActiveID()
else
local mouse_abs_pos = g.IO.MousePos[axis_k] local mouse_abs_pos = g.IO.MousePos[axis_k]
if g.ActiveIdIsJustActivated then if g.ActiveIdIsJustActivated then
local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) local grab_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize)
@@ -3891,11 +3934,6 @@ function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_
set_new_value = true set_new_value = true
end end
elseif g.ActiveIdSource == ImGuiInputSource.Keyboard or g.ActiveIdSource == ImGuiInputSource.Gamepad then elseif g.ActiveIdSource == ImGuiInputSource.Keyboard or g.ActiveIdSource == ImGuiInputSource.Gamepad then
if g.ActiveIdIsJustActivated then
g.SliderCurrentAccum = 0.0 -- Reset any stored nav delta upon activation
g.SliderCurrentAccumDirty = false
end
local input_delta = (axis == ImGuiAxis.X) and ImGui.GetNavTweakPressedAmount(axis) or -ImGui.GetNavTweakPressedAmount(axis) local input_delta = (axis == ImGuiAxis.X) and ImGui.GetNavTweakPressedAmount(axis) or -ImGui.GetNavTweakPressedAmount(axis)
if input_delta ~= 0.0 then if input_delta ~= 0.0 then
local tweak_slow = ImGui.IsKeyDown((g.NavInputSource == ImGuiInputSource.Gamepad) and ImGuiKey.NavGamepadTweakSlow or ImGuiKey.NavKeyboardTweakSlow) local tweak_slow = ImGui.IsKeyDown((g.NavInputSource == ImGuiInputSource.Gamepad) and ImGuiKey.NavGamepadTweakSlow or ImGuiKey.NavKeyboardTweakSlow)
@@ -3922,9 +3960,7 @@ function ImGui.SliderBehaviorT(TYPE, SIGNEDTYPE, bb, id, data_type, v, v_min, v_
end end
local delta = g.SliderCurrentAccum local delta = g.SliderCurrentAccum
if g.NavActivatePressedId == id and not g.ActiveIdIsJustActivated then if g.SliderCurrentAccumDirty then
ImGui.ClearActiveID()
elseif g.SliderCurrentAccumDirty then
clicked_t = ImGui.ScaleRatioFromValueT(data_type, v, v_min, v_max, logarithmic_zero_epsilon, zero_deadzone_halfsize) 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 -- This is to avoid applying the saturation when already past the limits if (clicked_t >= 1.0 and delta > 0.0) or (clicked_t <= 0.0 and delta < 0.0) then -- This is to avoid applying the saturation when already past the limits
@@ -4016,6 +4052,30 @@ function ImGui.SliderBehavior(bb, id, data_type, v, min, max, format, flags, out
IM_ASSERT((flags == 1 or bit32.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((flags == 1 or bit32.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(bit32.band(flags, ImGuiSliderFlags.WrapAround) == 0) -- Not supported by SliderXXX(), only by DragXXX() IM_ASSERT(bit32.band(flags, ImGuiSliderFlags.WrapAround) == 0) -- Not supported by SliderXXX(), only by DragXXX()
-- Things we can do easily outside the SliderBehaviorT<> template, saves code generation.
local g = GImGui
if g.ActiveId == id then
if g.ActiveIdSource == ImGuiInputSource.Mouse and not g.IO.MouseDown[0] then
ImGui.ClearActiveID()
end
if g.ActiveIdSource == ImGuiInputSource.Keyboard or g.ActiveIdSource == ImGuiInputSource.Gamepad then
if g.ActiveIdIsJustActivated then
g.SliderCurrentAccum = 0.0 -- Reset any stored nav delta upon activation
g.SliderCurrentAccumDirty = false
end
if g.NavActivatePressedId == id and not g.ActiveIdIsJustActivated then
ImGui.ClearActiveID()
end
end
if ShortcutsForCancel(id) and g.ActiveId == id then
-- Canceling action reverts to initial value
local value_changed = (v ~= g.ActiveIdValueOnActivation)
v = g.ActiveIdValueOnActivation
ImGui.ClearActiveID()
return v, value_changed
end
end
if data_type == ImGuiDataType.S8 then if data_type == ImGuiDataType.S8 then
local v32 = (ImS32)((ImS8)(v)); local r local v32 = (ImS32)((ImS8)(v)); local r
v32, r = ImGui.SliderBehaviorT(ImS32, ImS32, 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)
@@ -4602,7 +4662,7 @@ end
--- @param obj ImGuiInputTextState --- @param obj ImGuiInputTextState
--- @param idx int --- @param idx int
local function STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx) function ImStb.TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx)
local len = obj.TextLen local len = obj.TextLen
idx = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx) idx = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx)
while idx <= len and not ImStb.is_word_boundary_from_left(obj, idx) do while idx <= len and not ImStb.is_word_boundary_from_left(obj, idx) do
@@ -4613,7 +4673,7 @@ end
--- @param obj ImGuiInputTextState --- @param obj ImGuiInputTextState
--- @param idx int --- @param idx int
local function STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx) function ImStb.TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx)
idx = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx) idx = ImStb.TEXTEDIT_GETNEXTCHARINDEX(obj, idx)
local len = obj.TextLen local len = obj.TextLen
while idx <= len and not ImStb.is_word_boundary_from_right(obj, idx) do while idx <= len and not ImStb.is_word_boundary_from_right(obj, idx) do
@@ -4627,9 +4687,9 @@ end
function ImStb.TEXTEDIT_MOVEWORDRIGHT(obj, idx) function ImStb.TEXTEDIT_MOVEWORDRIGHT(obj, idx)
local g = obj.Ctx local g = obj.Ctx
if g.IO.ConfigMacOSXBehaviors then if g.IO.ConfigMacOSXBehaviors then
return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx) return ImStb.TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx)
else else
return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx) return ImStb.TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx)
end end
end end
@@ -4830,6 +4890,13 @@ function MT.ImGuiInputTextState:CursorClamp()
end end
function MT.ImGuiInputTextState:HasSelection() return self.Stb.select_start ~= self.Stb.select_end end function MT.ImGuiInputTextState:HasSelection() return self.Stb.select_start ~= self.Stb.select_end end
function MT.ImGuiInputTextState:ClearSelection() self.Stb.select_start = self.Stb.cursor; self.Stb.select_end = self.Stb.cursor end
-- [Luau] positions are 1-based byte indices (upstream: 0-based)
function MT.ImGuiInputTextState:GetCursorPos() return self.Stb.cursor end
function MT.ImGuiInputTextState:GetSelectionStart() return self.Stb.select_start end
function MT.ImGuiInputTextState:GetSelectionEnd() return self.Stb.select_end end
function MT.ImGuiInputTextState:SetSelection(start, _end) self.Stb.select_start = start; self.Stb.cursor = _end; self.Stb.select_end = _end end
function MT.ImGuiInputTextState:GetPreferredOffsetX() if self.Stb.has_preferred_x then return self.Stb.preferred_x else return -1 end end
function MT.ImGuiInputTextState:SelectAll() function MT.ImGuiInputTextState:SelectAll()
self.Stb.select_start = 1 self.Stb.select_start = 1
@@ -4838,6 +4905,10 @@ function MT.ImGuiInputTextState:SelectAll()
self.Stb.has_preferred_x = false self.Stb.has_preferred_x = false
end end
function MT.ImGuiInputTextState:ReloadUserBufAndSelectAll() self.WantReloadUserBuf = true; self.ReloadSelectionStart = 1; self.ReloadSelectionEnd = INT_MAX end
function MT.ImGuiInputTextState:ReloadUserBufAndKeepSelection() self.WantReloadUserBuf = true; self.ReloadSelectionStart = self.Stb.select_start; self.ReloadSelectionEnd = self.Stb.select_end end
function MT.ImGuiInputTextState:ReloadUserBufAndMoveToEnd() self.WantReloadUserBuf = true; self.ReloadSelectionStart = INT_MAX; self.ReloadSelectionEnd = INT_MAX end
end end
function ImGui.PushPasswordFont() function ImGui.PushPasswordFont()
@@ -4989,9 +5060,9 @@ local function InputTextFilterCharacter(ctx, state, char, callback, user_data, i
callback_data.EventFlag = ImGuiInputTextFlags.CallbackCharFilter callback_data.EventFlag = ImGuiInputTextFlags.CallbackCharFilter
callback_data.EventChar = c callback_data.EventChar = c
callback_data.EventActivated = (state ~= nil and g.ActiveId == state.ID and g.ActiveIdIsJustActivated) callback_data.EventActivated = (state ~= nil and g.ActiveId == state.ID and g.ActiveIdIsJustActivated)
callback_data.CursorPos = state.Stb.cursor callback_data.CursorPos = state.Stb.cursor - 1 -- [Luau] callback positions are 0-based
callback_data.SelectionStart = state.Stb.select_start callback_data.SelectionStart = state.Stb.select_start - 1
callback_data.SelectionEnd = state.Stb.select_end callback_data.SelectionEnd = state.Stb.select_end - 1
callback_data.UserData = user_data callback_data.UserData = user_data
if callback(callback_data) ~= 0 then if callback(callback_data) ~= 0 then
return char, false return char, false
@@ -5573,7 +5644,7 @@ function ImGui.InputTextEx(label, hint, buf, buf_size, size_arg, flags, callback
if not stbte.HAS_SELECTION(state.Stb) then if not stbte.HAS_SELECTION(state.Stb) then
stbte.prep_selection_at_cursor(state.Stb) stbte.prep_selection_at_cursor(state.Stb)
end end
state.Stb.cursor = STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state.Stb.cursor) state.Stb.cursor = ImStb.TEXTEDIT_MOVEWORDRIGHT_MAC(state, state.Stb.cursor)
state.Stb.select_end = state.Stb.cursor state.Stb.select_end = state.Stb.cursor
stbte.clamp(state, state.Stb) stbte.clamp(state, state.Stb)
else else
@@ -5902,9 +5973,9 @@ function ImGui.InputTextEx(label, hint, buf, buf_size, size_arg, flags, callback
callback_data.BufTextLen = state.TextLen callback_data.BufTextLen = state.TextLen
callback_data.BufSize = state.BufCapacity callback_data.BufSize = state.BufCapacity
callback_data.BufDirty = false callback_data.BufDirty = false
callback_data.CursorPos = state.Stb.cursor callback_data.CursorPos = state.Stb.cursor - 1 -- [Luau] callback positions are 0-based, stb ones 1-based
callback_data.SelectionStart = state.Stb.select_start callback_data.SelectionStart = state.Stb.select_start - 1
callback_data.SelectionEnd = state.Stb.select_end callback_data.SelectionEnd = state.Stb.select_end - 1
-- Call user code -- Call user code
callback(callback_data) callback(callback_data)
@@ -5914,12 +5985,12 @@ function ImGui.InputTextEx(label, hint, buf, buf_size, size_arg, flags, callback
IM_ASSERT(callback_data.Buf == callback_buf) -- Invalid to modify those fields IM_ASSERT(callback_data.Buf == callback_buf) -- Invalid to modify those fields
IM_ASSERT(callback_data.BufSize == state.BufCapacity) IM_ASSERT(callback_data.BufSize == state.BufCapacity)
IM_ASSERT(callback_data.Flags == flags) IM_ASSERT(callback_data.Flags == flags)
if callback_data.BufDirty or callback_data.CursorPos ~= state.Stb.cursor then if callback_data.BufDirty or callback_data.CursorPos ~= state.Stb.cursor - 1 then
state.CursorFollow = true state.CursorFollow = true
end end
state.Stb.cursor = ImClamp(callback_data.CursorPos, 0, callback_data.BufTextLen) state.Stb.cursor = ImClamp(callback_data.CursorPos, 0, callback_data.BufTextLen) + 1
state.Stb.select_start = ImClamp(callback_data.SelectionStart, 0, callback_data.BufTextLen) state.Stb.select_start = ImClamp(callback_data.SelectionStart, 0, callback_data.BufTextLen) + 1
state.Stb.select_end = ImClamp(callback_data.SelectionEnd, 0, callback_data.BufTextLen) state.Stb.select_end = ImClamp(callback_data.SelectionEnd, 0, callback_data.BufTextLen) + 1
if callback_data.BufDirty then if callback_data.BufDirty then
-- Callback may update buffer and thus set buf_dirty even in read-only mode. -- Callback may update buffer and thus set buf_dirty even in read-only mode.
IM_ASSERT(callback_data.BufTextLen == ImStd.ImStrlen(callback_data.Buf)) -- You need to maintain BufTextLen if you change the text! IM_ASSERT(callback_data.BufTextLen == ImStd.ImStrlen(callback_data.Buf)) -- You need to maintain BufTextLen if you change the text!
@@ -6283,6 +6354,47 @@ function ImGui.InputTextWithHint(label, hint, buf, buf_size, flags, callback, us
return ImGui.InputTextEx(label, hint, buf, buf_size, ImVec2(0, 0), flags, callback, user_data) return ImGui.InputTextEx(label, hint, buf, buf_size, ImVec2(0, 0), flags, callback, user_data)
end end
-- [Luau] stb positions/undo indices are 1-based in this port (displayed as-is)
--- @param state ImGuiInputTextState
function ImGui.DebugNodeInputTextState(state)
local g = GImGui
local stb_state = state.Stb
local undo_state = stb_state.undostate
ImGui.Text("ID: 0x%08X, ActiveID: 0x%08X", state.ID, g.ActiveId)
if ImGui.DebugLocateItemOnHover then ImGui.DebugLocateItemOnHover(state.ID) end -- [Luau] not ported yet
local wrap_dir = ""
if bit32.band(state.Flags, ImGuiInputTextFlags.WordWrap) ~= 0 then
if state.LastMoveDirectionLR == ImGuiDir.Left then wrap_dir = " (L)" else wrap_dir = " (R)" end
end
ImGui.Text("TextLen: %d, Cursor: %d%s, Selection: %d..%d", state.TextLen, stb_state.cursor, wrap_dir, stb_state.select_start, stb_state.select_end)
ImGui.Text("BufCapacity: %d, LineCount: %d", state.BufCapacity, state.LineCount)
ImGui.Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state.TextA.Size, state.TextA.Capacity)
ImGui.Text("has_preferred_x: %d (%.2f)", stb_state.has_preferred_x and 1 or 0, stb_state.preferred_x)
ImGui.Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state.undo_point, undo_state.redo_point, undo_state.undo_char_point, undo_state.redo_char_point)
if ImGui.BeginChild("undopoints", ImVec2(0.0, ImGui.GetTextLineHeight() * 10), bit32.bor(ImGuiChildFlags.Borders, ImGuiChildFlags.ResizeY)) then -- Visualize undo state
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, ImVec2(0, 0))
for n = 1, IMSTB_TEXTEDIT_UNDOSTATECOUNT do
local undo_rec = undo_state.undo_rec[n]
local undo_rec_type = (n < undo_state.undo_point) and "u" or ((n >= undo_state.redo_point) and "r" or " ")
if undo_rec_type == " " then
ImGui.BeginDisabled()
end
local buf_preview_len = (undo_rec_type ~= " " and undo_rec.char_storage ~= -1) and undo_rec.insert_length or 0
local buf_preview_str = ""
if buf_preview_len > 0 then
buf_preview_str = string.char(unpack(undo_state.undo_char, undo_rec.char_storage, undo_rec.char_storage + buf_preview_len - 1))
end
ImGui.Text("%s [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%s\"",
undo_rec_type, n, undo_rec.where, undo_rec.insert_length, undo_rec.delete_length, undo_rec.char_storage, buf_preview_str)
if undo_rec_type == " " then
ImGui.EndDisabled()
end
end
ImGui.PopStyleVar()
end
ImGui.EndChild()
end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. -- [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@@ -10648,14 +10760,37 @@ local function TabItemGetSectionIdx(tab)
return 1 return 1
end end
-- [Luau] comparers return a boolean "less than" for table.sort (upstream: ImQsort int comparers)
local function TabItemComparerBySection(a, b)
if a == nil or b == nil then return false end
local a_section = TabItemGetSectionIdx(a)
local b_section = TabItemGetSectionIdx(b)
if a_section ~= b_section then
return a_section < b_section
end
return a.IndexDuringLayout < b.IndexDuringLayout
end
local function TabItemComparerByBeginOrder(a, b)
return a.BeginOrder < b.BeginOrder
end
local function GetTabBarFromTabBarRef(ref)
local g = GImGui
if ref.Ptr then return ref.Ptr else return g.TabBars:GetByIndex(ref.Index) end
end
local function GetTabBarRefFromTabBar(tab_bar)
local g = GImGui
if g.TabBars:Contains(tab_bar) then
return ImGuiPtrOrIndex(g.TabBars:GetIndex(tab_bar))
end
return ImGuiPtrOrIndex(tab_bar)
end
-- Stable sorts (C++ ImQsort is not stable, but the comparers are total orders in practice) -- Stable sorts (C++ ImQsort is not stable, but the comparers are total orders in practice)
local function SortTabsBySection(tab_bar) local function SortTabsBySection(tab_bar)
table.sort(tab_bar.Tabs.Data, function(a, b) table.sort(tab_bar.Tabs.Data, TabItemComparerBySection)
if a == nil or b == nil then return false end
local sa, sb = TabItemGetSectionIdx(a), TabItemGetSectionIdx(b)
if sa ~= sb then return sa < sb end
return a.IndexDuringLayout < b.IndexDuringLayout
end)
end end
local function SortTabsByBeginOrder(tab_bar) local function SortTabsByBeginOrder(tab_bar)
@@ -10663,7 +10798,7 @@ local function SortTabsByBeginOrder(tab_bar)
if not data then return end if not data then return end
local arr = {} local arr = {}
for i = 1, tab_bar.Tabs.Size do arr[i] = data[i] end for i = 1, tab_bar.Tabs.Size do arr[i] = data[i] end
table.sort(arr, function(a, b) return a.BeginOrder < b.BeginOrder end) table.sort(arr, TabItemComparerByBeginOrder)
for i = 1, tab_bar.Tabs.Size do data[i] = arr[i] end for i = 1, tab_bar.Tabs.Size do data[i] = arr[i] end
end end
@@ -10677,6 +10812,12 @@ end
-- Spacing: Horizontal spacing at the end of the section. -- Spacing: Horizontal spacing at the end of the section.
local function ImGuiTabBarSection() return { TabCount = 0, Width = 0.0, WidthAfterShrinkMinWidth = 0.0, Spacing = 0.0 } end local function ImGuiTabBarSection() return { TabCount = 0, Width = 0.0, WidthAfterShrinkMinWidth = 0.0, Spacing = 0.0 } end
-- [Luau] boolean "less than" for table.sort. Width compared exactly: upstream's (int) truncation isn't a strict weak order, which table.sort rejects
local function ShrinkWidthItemComparer(a, b)
if a.Width ~= b.Width then return a.Width > b.Width end
return a.Index > b.Index -- b->Index - a->Index
end
-- Shrink excess width from a set of item, by removing width from the larger items first. -- Shrink excess width from a set of item, by removing width from the larger items first.
-- Set items Width to -1.0f to disable shrinking this item. -- Set items Width to -1.0f to disable shrinking this item.
-- items: 1-based array of ImGuiShrinkWidthItem, `first` = 1-based index of first item, `count` items -- items: 1-based array of ImGuiShrinkWidthItem, `first` = 1-based index of first item, `count` items
@@ -10690,10 +10831,7 @@ function ImGui.ShrinkWidths(items, first, count, width_excess, width_min)
-- Sort largest first, smallest last. -- Sort largest first, smallest last.
local slice = {} local slice = {}
for n = 1, count do slice[n] = items[first + n - 1] end for n = 1, count do slice[n] = items[first + n - 1] end
table.sort(slice, function(a, b) table.sort(slice, ShrinkWidthItemComparer)
if a.Width ~= b.Width then return a.Width > b.Width end
return a.Index > b.Index -- ShrinkWidthItemComparer: b->Index - a->Index (Width compared exactly: upstream's (int) truncation isn't a strict weak order, which table.sort rejects)
end)
for n = 1, count do items[first + n - 1] = slice[n] end for n = 1, count do items[first + n - 1] = slice[n] end
local it = slice local it = slice
local count_same_width = 1 local count_same_width = 1
@@ -10804,7 +10942,7 @@ function ImGui.BeginTabBarEx(tab_bar, tab_bar_bb, flags)
end end
-- Add to stack -- Add to stack
g.CurrentTabBarStack:push_back(tab_bar) g.CurrentTabBarStack:push_back(GetTabBarRefFromTabBar(tab_bar))
g.CurrentTabBar = tab_bar g.CurrentTabBar = tab_bar
tab_bar.Window = window tab_bar.Window = window
@@ -10886,7 +11024,7 @@ function ImGui.EndTabBar()
end end
g.CurrentTabBarStack:pop_back() g.CurrentTabBarStack:pop_back()
if g.CurrentTabBarStack.Size == 0 then g.CurrentTabBar = nil else g.CurrentTabBar = g.CurrentTabBarStack.Data[g.CurrentTabBarStack.Size] end if g.CurrentTabBarStack.Size == 0 then g.CurrentTabBar = nil else g.CurrentTabBar = GetTabBarFromTabBarRef(g.CurrentTabBarStack:back()) end
end end
-- This is called only once a frame before by the first call to ItemTab() -- This is called only once a frame before by the first call to ItemTab()
+2
View File
@@ -13,6 +13,8 @@ CHECKS=(
"readme-min|README OK|MINIFY=1 $R --main tools/test/scripts/t_readme.luau --frames 30" "readme-min|README OK|MINIFY=1 $R --main tools/test/scripts/t_readme.luau --frames 30"
"cached|CACHED OK|$R --main tools/test/scripts/t_cached_window.luau --frames 130" "cached|CACHED OK|$R --main tools/test/scripts/t_cached_window.luau --frames 130"
"fixes|FIXES OK|$R --main tools/test/scripts/t_fixes.luau --frames 55" "fixes|FIXES OK|$R --main tools/test/scripts/t_fixes.luau --frames 55"
"debugtools|DEBUGTOOLS OK|$R --main tools/test/scripts/t_debugtools.luau --frames 65"
"helpers|HELPERS OK|$R --main tools/test/scripts/t_helpers.luau --frames 35"
"multiselect|MULTISELECT OK|$R --main tools/test/scripts/t_multiselect.luau --frames 380" "multiselect|MULTISELECT OK|$R --main tools/test/scripts/t_multiselect.luau --frames 380"
"nodemo|NODEMO OK|NO_DEMO=1 $R --main tools/test/scripts/t_nodemo.luau --frames 25" "nodemo|NODEMO OK|NO_DEMO=1 $R --main tools/test/scripts/t_nodemo.luau --frames 25"
"widgets|OK|$R --main tools/test/scripts/t_widgets.luau --frames 80" "widgets|OK|$R --main tools/test/scripts/t_widgets.luau --frames 80"
+79
View File
@@ -0,0 +1,79 @@
-- Debug tools + misc imgui.cpp ports: item picker, DebugFlashStyleColor, DebugLocateItem, context hooks, AddInputCharactersUTF8, ImBezierCubicClosestPoint
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
local ImGui, ImVec2 = G.ImGui, G.ImVec2
local ctx = ImGui.CreateContext(); G.ImGui_ImplRoblox.Init()
local io = ImGui.GetIO()
local failed = {}
local function check(ok, msg) if not ok then table.insert(failed, msg); print("FAIL " .. msg) end end
-- ImBezierCubicClosestPoint: straight-line cubic (0,0)-(30,0)-(60,0)-(90,0): closest point to (45,10) is (45,0)
local p1, p2, p3, p4 = ImVec2(0, 0), ImVec2(30, 0), ImVec2(60, 0), ImVec2(90, 0)
local cp = G.ImBezierCubicClosestPoint(p1, p2, p3, p4, ImVec2(45, 10), 20)
check(math.abs(cp.x - 45) < 1e-3 and math.abs(cp.y) < 1e-6, ("ImBezierCubicClosestPoint %g,%g"):format(cp.x, cp.y))
-- Curved: result must lie on the curve (distance to a dense sampling of the curve ~0) and beat the endpoints
local q1, q2, q3, q4 = ImVec2(0, 0), ImVec2(0, 100), ImVec2(100, 100), ImVec2(100, 0)
local target = ImVec2(50, 120)
for _, r in { G.ImBezierCubicClosestPoint(q1, q2, q3, q4, target, 64), G.ImBezierCubicClosestPointCasteljau(q1, q2, q3, q4, target, 0.25) } do
check(math.abs(r.x - 50) < 0.5 and math.abs(r.y - 75) < 0.5, ("closest point on curve %g,%g (expected ~50,75)"):format(r.x, r.y))
end
-- AddInputCharactersUTF8 queues one Text event per codepoint
io:AddInputCharactersUTF8("a\u{e9}\u{20ac}\u{1F600}")
local chars = {}
for _, e in ctx.InputEventsQueue.Data do if e.Type == G.ImGuiInputEventType.Text then table.insert(chars, e.Text.Char) end end
-- (this build has IM_UNICODE_CODEPOINT_MAX = 0xFFFF like upstream's default ImWchar16: U+1F600 becomes IM_UNICODE_CODEPOINT_INVALID)
check(#chars == 4 and chars[1] == 0x61 and chars[2] == 0xE9 and chars[3] == 0x20AC and chars[4] == ((G.IM_UNICODE_CODEPOINT_MAX >= 0x1F600) and 0x1F600 or 0xFFFD), "AddInputCharactersUTF8 " .. table.concat(chars, ","))
-- Context hooks
local log = {}
local function hook(t, name)
local h = G.ImGuiContextHook(); h.Type = t; h.Callback = function(c, hk) table.insert(log, name) end
return ImGui.AddContextHook(ctx, h)
end
local T = G.ImGuiContextHookType
hook(T.NewFramePre, "nfpre"); hook(T.NewFramePost, "nfpost"); hook(T.RenderPre, "rpre"); local rpost = hook(T.RenderPost, "rpost")
-- Style color flash
local col = ImGui.GetStyle().Colors[G.ImGuiCol.Button]
local orig = { col.x, col.y, col.z, col.w }
local f, rect, picked, broke, button_id = 0, nil, nil, 0, 0
ctx.DebugBreakCallback = function() broke += 1 end
game:GetService("RunService").Stepped:Connect(function()
f += 1
G.ImGui_ImplRoblox.NewFrame()
if rect and f == 5 then io:AddMousePosEvent((rect[1].x + rect[2].x) / 2, (rect[1].y + rect[2].y) / 2) end
if f == 6 then ImGui.DebugStartItemPicker() end
if f == 8 then io:AddMouseButtonEvent(0, true) elseif f == 9 then io:AddMouseButtonEvent(0, false) end
ImGui.NewFrame()
if f == 1 then check(table.concat(log, " ") == "nfpre nfpost", "hooks after NewFrame: " .. table.concat(log, " ")) end
if f == 8 then picked = ctx.DebugItemPickerBreakId end
if f == 3 then ImGui.DebugFlashStyleColor(G.ImGuiCol.Button) end
ImGui.SetNextWindowPos(ImVec2(10, 10), G.ImGuiCond.Always)
ImGui.Begin("DebugTools")
ImGui.Button("Target")
if f == 2 then local a, b = ImGui.GetItemRectMin(), ImGui.GetItemRectMax(); rect = { ImVec2(a.x, a.y), ImVec2(b.x, b.y) } end
local target_id = ctx.LastItemData.ID
button_id = target_id
if f == 12 then ImGui.DebugLocateItem(target_id) end
if f == 13 then check(ctx.DebugLocateId == 0, "DebugLocateItem resolved by ItemAdd()") end
ImGui.DebugDrawItemRect(); ImGui.DebugDrawCursorPos(); ImGui.DebugDrawLineExtents()
ImGui.DebugTextUnformattedWithLocateItem(("id 0x%08X here"):format(target_id))
ImGui.End()
ImGui.SetNextWindowPos(ImVec2(400, 10), G.ImGuiCond.Always); ImGui.ShowMetricsWindow()
ImGui.SetNextWindowPos(ImVec2(400, 300), G.ImGuiCond.Always); ImGui.ShowDebugLogWindow()
if f == 4 then
local c = ImGui.GetStyle().Colors[G.ImGuiCol.Button]
check(c.w == 1.0 and ctx.DebugFlashStyleColorIdx == G.ImGuiCol.Button, "flash active")
end
ImGui.Render(); G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
if f == 1 then check(table.concat(log, " ") == "nfpre nfpost rpre rpost", "hooks after Render: " .. table.concat(log, " ")); ImGui.RemoveContextHook(ctx, rpost) end
if f == 2 then check(table.concat(log, " ") == "nfpre nfpost rpre rpost nfpre nfpost rpre", "RemoveContextHook: " .. table.concat(log, " ")) end
if f == 60 then
local c = ImGui.GetStyle().Colors[G.ImGuiCol.Button]
check(c.x == orig[1] and c.y == orig[2] and c.z == orig[3] and c.w == orig[4] and ctx.DebugFlashStyleColorIdx == nil, "flash restored color exactly")
check(picked ~= nil and picked ~= 0 and picked == button_id, ("item picker set DebugItemPickerBreakId (%s vs %s)"):format(tostring(picked), tostring(button_id)))
check(not ctx.DebugItemPickerActive and broke >= 1, ("item picker break hook called (%d)"):format(broke))
if #failed == 0 then print("DEBUGTOOLS OK") end
end
end)
+142
View File
@@ -0,0 +1,142 @@
-- Small upstream helpers: ImGuiInputTextState selection API + ReloadUserBuf*, ImGuiInputTextCallbackData:SetSelection, ImDrawList AddImageQuad/PrimQuadUV/
-- CloneOutput, ImDrawData DeIndexAllBuffers/ScaleClipRects, ImBitVector/ImBitArray*, ImGuiPackedDate, ImParseFormatSanitizeForPrinting, drag cancel (ShortcutsForCancel)
local G = require(game:GetService("ReplicatedStorage"):WaitForChild("ImGui"))
local ImGui, ImVec2 = G.ImGui, G.ImVec2
ImGui.CreateContext(); G.ImGui_ImplRoblox.Init()
local io = ImGui.GetIO()
local failed = {}
local function check(ok, msg) if not ok then table.insert(failed, msg); print("FAIL " .. msg) end end
-- Pure helpers
check(G.ImParseFormatSanitizeForPrinting("%.3f%%", nil, 32) == "%.3f", "sanitize %.3f%%")
check(G.ImParseFormatSanitizeForPrinting("%'_5.2f units", nil, 32) == "%5.2f", "sanitize stb flags: " .. G.ImParseFormatSanitizeForPrinting("%'_5.2f units", nil, 32))
local d = G.ImGuiPackedDate(20260315)
check(d:IsValid() and d:Unpack() == 20260315, "PackedDate round trip " .. d:Unpack())
d:SubtractMonths(3)
check(d:Unpack() == 20251215, "PackedDate SubtractMonths " .. d:Unpack())
check(not G.ImGuiPackedDate():IsValid() and G.ImGuiPackedDate():Unpack() == 0, "PackedDate empty")
local bv = G.ImBitVector()
bv:Create(70)
check(bv.Storage.Size == 3, "ImBitVector size")
bv:SetBit(1); bv:SetBit(70); bv:SetBit(33)
check(bv:TestBit(1) and bv:TestBit(70) and bv:TestBit(33) and not bv:TestBit(2) and not bv:TestBit(69), "ImBitVector SetBit/TestBit")
bv:ClearBit(33)
check(not bv:TestBit(33) and bv:TestBit(70), "ImBitVector ClearBit")
local arr = {}
G.ImBitArrayClearAllBits(arr, 96)
G.ImBitArraySetBitRange(arr, 3, 70) -- [3..70)
local ok_range = true
for n = 1, 96 do if G.ImBitArrayTestBit(arr, n) ~= (n >= 3 and n < 70) then ok_range = false end end
check(ok_range, "ImBitArraySetBitRange")
check(G.ImBitArrayGetStorageSizeInBytes(70) == 12, "ImBitArrayGetStorageSizeInBytes")
check(G.ImModPositive(-1, 5) == 4 and G.ImRoundSigned64(-1.5) == -2 and G.ImCharIsXdigitA(string.byte("F")), "math helpers")
local buf = ImGui.NewTextBuffer("hello world", 64)
local cbuf = ImGui.NewTextBuffer("abcdef", 64)
local cb_calls = 0
local function cb(data)
cb_calls += 1
if cb_calls == 1 then data:SetSelection(1, 3) end
return 0
end
local df, rect_df = 10.0, nil
local f = 0
local function center(r) return (r[1].x + r[2].x) / 2, (r[1].y + r[2].y) / 2 end
local function vtx(dl, i) -- 1-based vertex
local b, o = dl.VtxBuffer.Buf, (i - 1) * G.VTX_STRIDE
return buffer.readf64(b, o), buffer.readf64(b, o + 8), buffer.readf64(b, o + 16), buffer.readf64(b, o + 24), buffer.readu32(b, o + 32)
end
game:GetService("RunService").Stepped:Connect(function()
f += 1
G.ImGui_ImplRoblox.NewFrame()
-- drag + Escape cancels and reverts (ShortcutsForCancel)
if f == 20 then io:AddMousePosEvent(center(rect_df)) end
if f == 21 then io:AddMouseButtonEvent(0, true) end
if f == 22 then local x, y = center(rect_df); io:AddMousePosEvent(x + 40, y) end
if f == 24 then io:AddKeyEvent(G.ImGuiKey.Escape, true) end
if f == 25 then io:AddKeyEvent(G.ImGuiKey.Escape, false) end
if f == 26 then io:AddMouseButtonEvent(0, false) end
ImGui.NewFrame()
ImGui.SetNextWindowPos(ImVec2(10, 10)); ImGui.SetNextWindowSize(ImVec2(400, 300))
ImGui.Begin("H")
if f == 2 then ImGui.SetKeyboardFocusHere() end
ImGui.InputText("##t", buf, 64)
local id = ImGui.GetItemID()
local st = ImGui.GetInputTextState(id)
if f == 5 then
check(st ~= nil, "InputText active")
if st then
st:SetSelection(3, 6)
check(st:GetSelectionStart() == 3 and st:GetSelectionEnd() == 6 and st:GetCursorPos() == 6 and st:HasSelection(), "SetSelection/Get*")
st:ClearSelection()
check(st:GetSelectionStart() == 6 and st:GetSelectionEnd() == 6 and not st:HasSelection(), "ClearSelection")
check(st:GetPreferredOffsetX() == -1, "GetPreferredOffsetX")
-- modify the user buffer externally then reload
local s = "bye"; for i = 1, #s do buf[i] = string.byte(s, i) end; buf[#s + 1] = 0
st:ReloadUserBufAndSelectAll()
end
end
if f == 7 and st then
check(G.ImGui._ByteArrayToString(st:GetText(), 1, st.TextLen + 1) == "bye", "ReloadUserBufAndSelectAll text")
check(st:GetSelectionStart() == 1 and st:GetSelectionEnd() == 4, ("ReloadUserBufAndSelectAll sel %d..%d"):format(st:GetSelectionStart(), st:GetSelectionEnd()))
st:SetSelection(2, 3); st:ReloadUserBufAndKeepSelection()
end
if f == 9 and st then
check(st:GetSelectionStart() == 2 and st:GetSelectionEnd() == 3, "ReloadUserBufAndKeepSelection")
st:ReloadUserBufAndMoveToEnd()
end
if f == 11 and st then
check(st:GetCursorPos() == 4 and not st:HasSelection(), "ReloadUserBufAndMoveToEnd " .. st:GetCursorPos())
end
-- callback SetSelection (0-based callback positions -> 1-based stb)
if f == 12 then ImGui.SetKeyboardFocusHere() end
ImGui.InputText("##c", cbuf, 64, G.ImGuiInputTextFlags.CallbackAlways, cb)
if f == 15 then
local cst = ImGui.GetInputTextState(ImGui.GetItemID())
check(cst ~= nil and cb_calls > 0 and cst:GetSelectionStart() == 2 and cst:GetSelectionEnd() == 4, "callback SetSelection " .. (cst and (cst:GetSelectionStart() .. ".." .. cst:GetSelectionEnd()) or "nil"))
end
df = ImGui.DragFloat("##df", df, 1.0)
local a, b = ImGui.GetItemRectMin(), ImGui.GetItemRectMax(); rect_df = { ImVec2(a.x, a.y), ImVec2(b.x, b.y) }
if f == 23 then check(df ~= 10.0, "drag changed value " .. df) end
if f == 25 then check(df == 10.0 and ImGui.GetActiveID() == 0, "Escape reverts drag " .. df) end
if f == 30 then
local dl = ImGui.GetWindowDrawList()
local v0, i0 = dl.VtxBuffer.Size, dl.IdxBuffer.Size
local tex = G.ImTextureRef(1234)
dl:AddImageQuad(tex, ImVec2(10, 10), ImVec2(50, 12), ImVec2(48, 60), ImVec2(8, 58), ImVec2(0, 0), ImVec2(1, 0), ImVec2(1, 1), ImVec2(0, 1), 0xFF00FF00)
check(dl.VtxBuffer.Size == v0 + 4 and dl.IdxBuffer.Size == i0 + 6, "AddImageQuad counts")
local x, y, u, v, col = vtx(dl, v0 + 3)
check(x == 48 and y == 60 and u == 1 and v == 1 and col == 0xFF00FF00, ("PrimQuadUV vertex %s %s %s %s %x"):format(x, y, u, v, col))
local idx = dl.IdxBuffer.Data
check(idx[i0 + 1] == v0 + 1 and idx[i0 + 5] == v0 + 3 and idx[i0 + 6] == v0 + 4, "PrimQuadUV indices")
local cmin, cmax = dl:GetClipRectMin(), dl:GetClipRectMax()
check(cmax.x > cmin.x and cmax.y > cmin.y, "GetClipRectMin/Max")
local clone = dl:CloneOutput()
check(clone.VtxBuffer.Size == dl.VtxBuffer.Size and clone.IdxBuffer.Size == dl.IdxBuffer.Size and clone.CmdBuffer.Size == dl.CmdBuffer.Size
and clone.VtxBuffer.Buf ~= dl.VtxBuffer.Buf and clone.CmdBuffer.Data[1] ~= dl.CmdBuffer.Data[1], "CloneOutput copies buffers")
local cx = vtx(clone, v0 + 3)
check(cx == 48, "CloneOutput vertex data")
local dd = G.ImDrawData()
dd.CmdLists:push_back(clone)
local n_idx = clone.IdxBuffer.Size
local ex, ey = vtx(clone, clone.IdxBuffer.Data[n_idx])
dd:DeIndexAllBuffers()
check(clone.IdxBuffer.Size == 0 and clone.VtxBuffer.Size == n_idx and dd.TotalVtxCount == n_idx and dd.TotalIdxCount == 0, "DeIndexAllBuffers counts")
local lx, ly = vtx(clone, n_idx)
check(lx == ex and ly == ey, "DeIndexAllBuffers vertex data")
local cr = clone.CmdBuffer.Data[1].ClipRect
local x1, y1, z1, w1 = cr.x, cr.y, cr.z, cr.w
dd:ScaleClipRects(ImVec2(2, 3))
local cr2 = clone.CmdBuffer.Data[1].ClipRect
check(cr2.x == x1 * 2 and cr2.y == y1 * 3 and cr2.z == z1 * 2 and cr2.w == w1 * 3 and dl.CmdBuffer.Data[1].ClipRect.z == z1, "ScaleClipRects")
end
ImGui.End()
ImGui.Render(); G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
if f == 32 and #failed == 0 then print("HELPERS OK") end
end)